R Data Visualistion 1 - Objective of workshop

To create scatter and bar plot visualisations using the ggplot2 package.

What this workshop will cover

In this workshop, the aim is to cover how to use the ggplot2 package. We will be covering:

  • An introduction to the ggplot2 package
  • How to make scatter plots with ggplot2
  • How to make bar plots with ggplot2
  • How to change colours and other features in your visualisations

Introduction

Data visualisation is a way of looking at your data using graphics, which provides a different perspective to your data.

There are a lot of different options for data visualistion with R. You can use the visualisation tools that come with R, ggplot and all its extensions, or for interactive visualisations there is the plotly library.

In this data visualisation series we will be mainly focussing on ggplot, as well as plotly. While the visualistion tools that come with R are useful, ggplot and plotly are generally easier to use and make great visualisations with. For this tutorial we will be using the below packages: ggplot2, dplyr, readr, janitor. Run the code below to install the packages if you don’t have them installed already.

# install packages
install <- c("ggplot2", "dplyr", "readr",
             "janitor", "RColorBrewer",
             "forcats")

install.packages(install, Ncpus = 6)

Then we need to load them into our session. Run the code chunk below to load all the libraries you will need.

# load packages
library(ggplot2)
library(dplyr)
library(readr)
library(janitor)
library(RColorBrewer)
library(forcats)

What is ggplot and how does it work?

ggplot2 is a package for producing graphics that works by combining independent components when making graphs, known as layers. This makes ggplot2 both versatile and powerful; you are not limited by a set of options but instead can make novel graphics to suit your needs.

It is also important to note that ggplot can only use data frames. If your data is in another format you will need to transform it into a data frame in order to use ggplot.

In order to understand how the layers work we will first load in some data for our examples. We will use data from the Pokémon games, which was web scraped from https://pokemondb.net/pokedex/all.

# load and clean names
pokemon <- read_csv("https://raw.githubusercontent.com/andrewmoles2/webScraping/main/R/data/pokemon.csv") %>%
  clean_names()
# review data
pokemon %>%
  glimpse()
## Rows: 952
## Columns: 13
## $ number     <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, …
## $ name       <chr> "Bulbasaur", "Ivysaur", "Venusaur", "Charmander", "Charmele…
## $ type1      <chr> "Grass", "Grass", "Grass", "Fire", "Fire", "Fire", "Water",…
## $ type2      <chr> "Poison", "Poison", "Poison", NA, NA, "Flying", NA, NA, NA,…
## $ total      <dbl> 318, 405, 525, 309, 405, 534, 314, 405, 530, 195, 205, 395,…
## $ hp         <dbl> 45, 60, 80, 39, 58, 78, 44, 59, 79, 45, 50, 60, 40, 45, 65,…
## $ attack     <dbl> 49, 62, 82, 52, 64, 84, 48, 63, 83, 30, 20, 45, 35, 25, 90,…
## $ defense    <dbl> 49, 63, 83, 43, 58, 78, 65, 80, 100, 35, 55, 50, 30, 50, 40…
## $ sp_atk     <dbl> 65, 80, 100, 60, 80, 109, 50, 65, 85, 20, 25, 90, 20, 25, 4…
## $ sp_def     <dbl> 65, 80, 100, 50, 65, 85, 64, 80, 105, 20, 25, 80, 20, 25, 8…
## $ speed      <dbl> 45, 60, 80, 65, 80, 100, 43, 58, 78, 45, 30, 70, 50, 35, 75…
## $ legendary  <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FAL…
## $ generation <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…

The syntax for ggplot has three key components. The ggplot function call (ggplot()), the aesthetics (called aes()), and the geometry (called geoms) which refers to scatter, bar, or line plots for example. The next three code chunks break this down.

# call ggplot2 and add data
ggplot(pokemon)

Notice we just get a grey box. We have just loaded our data into ggplot but not much else! Now lets add the aesthetics and see what happens.

Too add aesthetics we use the aes() function within the ggplot() function, and specify what our x and y axis will be with column names from our data, sp_atk and sp_def in this case.

# add aesthetics
ggplot(pokemon, aes(x = sp_atk, y = sp_def))

It is starting to look more like a visualisation now, we can see the x and y axis labels, but we still have no data points showing. We have to add a geometry for that to happen. Notice the syntax here, we use the + icon to add a geometry to ggplot, which in this case is geom_point() which makes scatter plots. All geometry functions start with geom_ and end with the type of geometry such as point, bar, or line.

# pick which geometry
ggplot(pokemon, aes(x = sp_atk, y = sp_def)) +
  geom_point()

This is the fundamental concept of ggplot, you construct your visualisations in layers, adding geometry layers, and other features as you go.

what is ggplot exercise

Using the pokemon data, make a scatter plot with hp on the x axis and speed on the y axis.

# your code here

Scatter plots

Scatter plots are for displaying the relationship between two numeric (or quantitative) variables. For each data point, the values of its first variable is represented on the X axis and the second on the Y axis.

To make a scatter plot with ggplot2 we use the geom_point() function like you just saw. In order for ggplot to make a scatter plot, the X and Y axis must be numeric.

The plot we just made in the example is okay but it could do with some improving. There are quite a few different ways to change the appearance of a visualisation, lets go through them.

The first thing we will look at is adding some colour! There are a few options for adding colours to your plots. You can add the name, such as red, or you can use a hex code, or you can use a pre-defined palette. To add colour to a scatter plot we use the colour = argument.

# colour of points
ggplot(pokemon, aes(x = hp, y = speed)) +
  geom_point(colour = "orange")

To colour your points by a group (or factor) we have to add the colour argument into the aes() function. This allows us to have different colours for different groups, which makes the plot more informative.

In the below example, our data is coloured by if a pokemon is classified as legendary or not.

# colour by factor
ggplot(pokemon, aes(x = hp, y = speed, colour = legendary)) +
  geom_point()

We get the default ggplot colours which are okay. There are a few different ways of changing the colours, all methods use the scale_ function in a slightly different way. In the two examples below we have changed the colours using the RColorBrewer package and have set the colours manually.

RColorBrewer comes with a set of palettes for different situations, you can view them by following this link https://www.r-graph-gallery.com/38-rcolorbrewers-palettes.html. To use these palettes with ggplot we use the scale_colour_brewer() function with an argument for which palette we want to use; in this example we are using Set1.

library(RColorBrewer)
# adjusting colour by factor using RColorBrewer
ggplot(pokemon, aes(x = hp, y = speed, colour = legendary)) +
  geom_point() +
  scale_colour_brewer(palette = "Set1")

To make a manual palette, you first make a vector with your colours, to do so it is useful to use a colour picker such as http://tristen.ca/hcl-picker/#/hlc/6/1/15534C/E2E062 or https://coolors.co/. You copy the hex code (code with # then 6 numbers of letters) and paste it into your vector like you can see in the manual_pal vector below. To add the colour we use scale_colour_manual() function, and set the values to our manual palette.

# adjusting colour by factor using manual palette
manual_pal <- c("#90C0F8", "#EA964E")

ggplot(pokemon, aes(x = hp, y = speed, colour = legendary)) +
  geom_point() +
  scale_colour_manual(values = manual_pal)

It is sometimes helpful to view the palette before using it. We can use the scales package for this, which is installed when you install ggplot2. We provide the show_col() function with the palette we want to view and it returns a grid view of the colours. In the example we look at Set1 from RColorBrewer and the manual palette we just used.

# load scales
library(scales)
## 
## Attaching package: 'scales'
## The following object is masked from 'package:readr':
## 
##     col_factor
# view palettes
show_col(RColorBrewer::brewer.pal(n = 8, name = "Set1"))

show_col(manual_pal)

As well as changing the colour of the points, you can change their shape, size, and transparency (alpha). Just like with colour, we can define the size, shape or transparency either in the aes() function or in a geom_ function. By adding them to the geom_ function we manually change them. If we use them in aes() we have to associate the size/shape/alpha with a variable.

See the below example, first we manually set the size and alpha. In the second example we set the size to be defined by the total column in our pokemon data, and manually set the alpha.

# manually set size and alpha
ggplot(pokemon, aes(x = hp, y = speed, colour = legendary)) +
  geom_point(size = 5, alpha = 0.6) +
  scale_colour_brewer(palette = "Set1")

# manually set alpha, size by total
ggplot(pokemon, aes(x = hp, y = speed, colour = legendary, size = total)) +
  geom_point(alpha = 0.6) +
  scale_colour_brewer(palette = "Set1")

To manually change the shape and replace the circles, we give the shape argument a number. Each number represents a shape, letter, or number; by default ggplot uses shape number 19. We can change the shape to a square for example by using the number 15.

# default shape number
ggplot(pokemon, aes(x = hp, y = speed, colour = legendary, size = total)) +
  geom_point(alpha = 0.6, shape = 19) +
  scale_colour_brewer(palette = "Set1")

# shape number for squares
ggplot(pokemon, aes(x = hp, y = speed, colour = legendary, size = total)) +
  geom_point(alpha = 0.6, shape = 15) +
  scale_colour_brewer(palette = "Set1")

View the image with the visual markdown editor to see what number represents what shape, letter, or number.

Finally we can add a title and save our plot! We’ve done two things in order to achieve this. To add a title, and change axis labels, we have used the labs() function. We add arguments for what we want to change, such as title = "Pokemon Hit Points vs Speed". To change the legend labels we use colour and size, as we used these to define our legend in the aes() function.

To save the plot we assign our code to a variable, then we use the ggsave() function, which requires what you want to call the file and the file extension (e.g. plot.PNG or plot.JPG), then the ggplot object we created. Run the example below, and you should get a hp_vs_speed.PNG file where your Rmd file is saved. You can also adjust the size of the image saved using the width and height arguments.

# save plot to a variable
hp_vs_speed <- ggplot(pokemon, aes(x = hp, y = speed, colour = legendary, size = total)) +
  geom_point(alpha = 0.6, shape = 15) +
  scale_colour_brewer(palette = "Set1") +
  labs(title = "Pokemon Hit Points vs Speed",
       subtitle = "Taken from pokemondb.net",
       x = "Hit Points",
       y = "Speed",
       colour = "Legenary pokemon?",
       size = "Total stats")

hp_vs_speed

# save plot
ggsave("hp_vs_speed.PNG", hp_vs_speed)
## Saving 7 x 5 in image
# save with defined width and height
ggsave("hp_vs_speed.PNG", hp_vs_speed,
       width = 7, height = 4.5)

Scatter plots exercise

For the exercises in this workshop we will use data from the Olympics that includes all Olympic games from 1896 through to 2016. More information on the dataset can be found here https://github.com/rfordatascience/tidytuesday/blob/master/data/2021/2021-07-27/readme.md. Run the code provided to load the libraries and data into R.

We will make two scatter plots from the Olympics data. For both plots we will use dplyr to filter the information we are interested in, which has been done for you in this exercise.

  1. Using the provided scatter_plot1 data, make a scatter plot of Olympic gymnasts heights (x axis) and weights (y axis).
  • Change the colour and shape arguments to tell us what sex the gymnasts are.
  • Change the colour palette by making a manual one or using RColorBrewer.
  • Be sure to give your plot a title, and save your plot.
  1. Using the provided scatter_plot2 data, make a scatter plot of the age (y axis) of gymnastic medal winners by year (x axis).
  • Colour your plot by medal by making a manual colour palette. hint: the hex codes for gold, silver and bronze are: “#FFD700”, “#C0C0C0”, “#CD7F32”
  • Use shape to tell us what sex the gymnasts were.
  • Be sure to give your plot a title, and save your plot.
# make sure libraries are loaded
library(readr)
library(dplyr)
library(ggplot2)
library(RColorBrewer)

# load in data
olympics <- read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-07-27/olympics.csv")
## Rows: 271116 Columns: 15
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (10): name, sex, team, noc, games, season, city, sport, event, medal
## dbl  (5): id, age, height, weight, year
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
olympics %>% glimpse()
## Rows: 271,116
## Columns: 15
## $ id     <dbl> 1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, …
## $ name   <chr> "A Dijiang", "A Lamusi", "Gunnar Nielsen Aaby", "Edgar Lindenau…
## $ sex    <chr> "M", "M", "M", "M", "F", "F", "F", "F", "F", "F", "M", "M", "M"…
## $ age    <dbl> 24, 23, 24, 34, 21, 21, 25, 25, 27, 27, 31, 31, 31, 31, 33, 33,…
## $ height <dbl> 180, 170, NA, NA, 185, 185, 185, 185, 185, 185, 188, 188, 188, …
## $ weight <dbl> 80, 60, NA, NA, 82, 82, 82, 82, 82, 82, 75, 75, 75, 75, 75, 75,…
## $ team   <chr> "China", "China", "Denmark", "Denmark/Sweden", "Netherlands", "…
## $ noc    <chr> "CHN", "CHN", "DEN", "DEN", "NED", "NED", "NED", "NED", "NED", …
## $ games  <chr> "1992 Summer", "2012 Summer", "1920 Summer", "1900 Summer", "19…
## $ year   <dbl> 1992, 2012, 1920, 1900, 1988, 1988, 1992, 1992, 1994, 1994, 199…
## $ season <chr> "Summer", "Summer", "Summer", "Summer", "Winter", "Winter", "Wi…
## $ city   <chr> "Barcelona", "London", "Antwerpen", "Paris", "Calgary", "Calgar…
## $ sport  <chr> "Basketball", "Judo", "Football", "Tug-Of-War", "Speed Skating"…
## $ event  <chr> "Basketball Men's Basketball", "Judo Men's Extra-Lightweight", …
## $ medal  <chr> NA, NA, NA, "Gold", NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
# data cleaning for first scatter plot
scatter_plot1 <- olympics %>%
  filter(sport == "Gymnastics")

# data cleaning for second scatter plot
scatter_plot2 <- olympics %>%
  filter(sport == "Gymnastics") %>%
  filter(!is.na(medal)) %>%
  mutate(medal = factor(medal, levels = c("Gold", "Silver", "Bronze")))

# your code here

Quirks of ggplot2

There are a few quirks to be aware of when using ggplot2 and you’ll see a few of them when you look for examples online. In order to aid with this, we can have a look at a few of them!

The first quirk is piping data into ggplot, where you do not need to add your data into the ggplot() function as it is piped in. The main advantage of this approach is you can string together some data cleaning and then pipe the results straight into ggplot.

# piping data into ggplot
pokemon %>%
  ggplot(aes(x = sp_atk, y = sp_def)) +
  geom_point()

# piping with filter
pokemon %>%
  filter(type1 == "Fire") %>%
  ggplot(aes(x = sp_atk, y = sp_def)) +
  geom_point()

The second quirk is adding aesthetics into a geom_ function rather than the ggplot() function.

# adding aesthetics into the geom_ call
ggplot(pokemon) +
  geom_point(aes(x = sp_atk, y = sp_def))

The third quirk is you can also add the data into the geom_ function. When doing so you have to have data = otherwise you will get an error.

# adding data and aesthetics into the geom_ call
ggplot() +
  geom_point(data = pokemon, aes(x = sp_atk, y = sp_def))

The fourth quirk relates to the second and third, in that you can add aesthetics into a geom_ function more than once. You might occasionally come across this for more complex visualisations.

In the example we will add the average of our x and y variables. First we make a summary table that has the averages of both axis’s, using summarise() from dplyr. Then we add two geom_point() functions, one with the pokemon data, and one with our summary table data.

# why adding aesthetics into the geom_ call
# calculate mean of sp_atk and sp_def
avg_sp <- pokemon %>%
  summarise(
    avg_sp_atk = mean(sp_atk, na.rm = TRUE),
    avg_sp_def = mean(sp_def, na.rm = TRUE))

avg_sp
## # A tibble: 1 × 2
##   avg_sp_atk avg_sp_def
##        <dbl>      <dbl>
## 1       71.3       70.7
# add average sp_atk and sp_def as black point
ggplot() +
  geom_point(data = pokemon, 
             aes(x = sp_atk, y = sp_def), 
             colour = "orange",
             size = 2.5) +
  geom_point(data = avg_sp, 
             aes(x = avg_sp_atk, y = avg_sp_def),
             size = 2.5)

The last quirk we will look at is adding to a ggplot visualisation after you have assigned it a name. This is very common in tutorials and on Stack Overflow. A good use of this is to build a base of the x and y you want to use and test out different geometries.

# saving plot then adding to it
p <- ggplot(pokemon, aes(x = sp_atk, y = sp_def))

p

p + geom_point()

p + geom_line()

Quirks of ggplot2 exercise

Make a visualisation of USA athletes ages vs heights, showing the difference between the genders using colour. When making your visualisation try to

  • Pipe the olympics data to a filter function and select all USA athletes
  • Pipe to a ggplot function
  • Add a geom_point function and add the aesthetics there rather than in ggplot()
# your code here

Bar plots with counts

Bar plots are used to show relationships between a numerical and categorical variable. The categorical variable is usually on the x axis, and the y axis is usually a frequency count.

By default, bar plots with ggplot only require an x or y axis. From that they make a frequency count of that variable. See the example below. First we use ggplot to make a bar plot to count the number of pokemon added in each generation. Then we do the same thing with dplyr to make a aggregate table, ggplot is taking this aggregate table and making into a plot for us!

It is important to make sure your x axis in a bar plot is a factor, as this helps ggplot to order the axis as you expect.

# make generation a factor
pokemon$generation <- factor(pokemon$generation)

# default bar plot
ggplot(pokemon, aes(x = generation)) +
  geom_bar()

# dplyr aggregate equivalent
pokemon %>%
  count(generation)
## # A tibble: 8 × 2
##   generation     n
##   <fct>      <int>
## 1 1            151
## 2 2             99
## 3 3            141
## 4 4            115
## 5 5            165
## 6 6             84
## 7 7             99
## 8 8             98

To add colour to your bar plot we use the fill argument rather than colour. This can be confusing, and sometimes if you forget, just try both till the colours look right! To add our fill manually we add the fill command to our geom_bar() function.

# manually add fill colour
ggplot(pokemon, aes(generation)) +
  geom_bar(fill = "purple")

Just like with the scatter plot, we can colour our plot by a variable by putting the fill argument within the aes() function. The below example also shows the equivalent when doing aggregation using dplyr.

# bar plot with colour by variable
ggplot(pokemon, aes(x = generation, fill = legendary)) +
  geom_bar()

# dplyr aggregate equivalent
pokemon %>%
  count(generation, legendary)
## # A tibble: 16 × 3
##    generation legendary     n
##    <fct>      <lgl>     <int>
##  1 1          FALSE       146
##  2 1          TRUE          5
##  3 2          FALSE        94
##  4 2          TRUE          5
##  5 3          FALSE       128
##  6 3          TRUE         13
##  7 4          FALSE       100
##  8 4          TRUE         15
##  9 5          FALSE       145
## 10 5          TRUE         20
## 11 6          FALSE        75
## 12 6          TRUE          9
## 13 7          FALSE        69
## 14 7          TRUE         30
## 15 8          FALSE        82
## 16 8          TRUE         16

Notice in the above example that the bars by default were stacked on top of each other. We have two other options for changing this with either a dodge setting (sit next to each other) or a fill setting (stacked and standarised). To change this we use the position argument within geom_bar().

# dodge bars
ggplot(pokemon, aes(x = generation, fill = legendary)) +
  geom_bar(position = "dodge")

# filled bars
ggplot(pokemon, aes(x = generation, fill = legendary)) +
  geom_bar(position = "fill")

A useful thing to change with bar plots is to flip your coordinates. This is particularly useful if your x axis contains text. In the example below we will use the type1 variable as our x axis to see the difference. When we don’t flip the coordinates, the x axis is hard to read.

ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar()

ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar() + 
  coord_flip()

To change our colours we use the scale_fill_ function. This is very similar to what we did with scatter plots except we are using fill this time, rather than colour.

# change fill with RColorBrewer
ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar() + 
  coord_flip() +
  scale_fill_brewer(palette = "Set1")

# change fill with manual palette
ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar() + 
  coord_flip() +
  scale_fill_manual(values = manual_pal)

Currently our plots have the default ggplot theme which has a grey background. We can change this by setting a new theme. To do so you use theme_ and select a theme which works best.

# change theme to black and white
ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar() + 
  coord_flip() +
  scale_fill_manual(values = manual_pal) +
  theme_bw()

# change theme to dark
ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar() + 
  coord_flip() +
  scale_fill_manual(values = manual_pal) +
  theme_dark()

Adding a theme to each plot can be tiring, so instead you can set a theme for all your plots by using the theme_set() function. Usually you set the theme before you make any of your visualisations. Now we have changed the theme to black and white, all our plots from now on will have a black and white theme.

# set global theme
theme_set(theme_bw())

# see result
ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar() + 
  coord_flip() +
  scale_fill_manual(values = manual_pal)

It is often useful and helpful to arrange the values by their rank or size. There are options to do this with base R, but the forcats library from the tidyverse makes arranging and ordering functions very straightforward.

We will use the fct_infreq() function, which means factors in frequency, in effect ordering our factors by the frequency they appear. There are two approaches. First we use the fct_infreq() function within ggplot, or second we arrange our factor outside ggplot. Outside of ggplot is usually better as you have more control and it make your ggplot code easier to read.

# load forcats
library(forcats)

# arrange by frequency within ggplot
ggplot(pokemon, aes(x = fct_infreq(type1), fill = legendary)) +
  geom_bar() + 
  coord_flip() +
  scale_fill_manual(values = manual_pal)

# arrange by frequency outside ggplot
pokemon$type1 <- fct_infreq(pokemon$type1)

ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar() + 
  coord_flip() +
  scale_fill_manual(values = manual_pal)

We can also reverse the ordering by putting putting our fct_infreq() function inside a fct_rev() function (stands for factor reverse).

# arrange by frequency (descending)
pokemon$type1 <- fct_rev(fct_infreq(pokemon$type1))

ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar() + 
  coord_flip() +
  scale_fill_manual(values = manual_pal)

More information on the forcats package can be found here: https://forcats.tidyverse.org/index.html

Finally, let’s save and label our example bar plot.

# save and label
count_type1 <- ggplot(pokemon, aes(x = type1, fill = legendary)) +
  geom_bar() + 
  coord_flip() +
  scale_fill_manual(values = manual_pal) +
  labs(title = "Frequency of each Pokemon type",
       subtitle = "Coloured by if legendary or not",
       y = "Frequency of Pokemon type",
       x = "Type of Pokemon",
       fill = "Legendary pokemon?")

count_type1

ggsave("count_type1.PNG", count_type1)
## Saving 7 x 5 in image

Bar plots with counts exercise

Using the examples above, make a visualisation of the frequency of ski jump medal winners per country (team) from the Olympics dataset.

Try to include:

  • Setting a new theme using theme_set().
  • Order the x axis by the frequency in reverse order. hint: remember the forcats package
  • Make medals a factor, re-order them, and then colour them like we did in the last exercise.
  • Decide if position stack, dodge or fill work best with this visualisation.
  • Add a title and labels.
  • Save your visualisation.
# your code here

Bar plots with other statistics

A very useful function of bar plots is to show a group average instead of frequency. There are two approaches to showing a group average in a bar plot.

The first route is aggregate your dataset, then add it into your bar plot as shown in the example below. We first use group_by() and summarise() from dplyr to find an average, in this case the average total statistics by pokemon generation.

We then put this data into ggplot. The difference from a normal bar plot is we provide a y axis (our calculated average), and add stat = "identity" to the geom_bar() function.

This is a great approach as it is easy to see what is happening at each step, making it simple to identify issues and make changes if needed.

# group and summarise to make average
avg_total_gen <- pokemon %>%
  group_by(generation) %>%
  summarise(avg_total = mean(total, na.rm = TRUE))

# print result
avg_total_gen
## # A tibble: 8 × 2
##   generation avg_total
##   <fct>          <dbl>
## 1 1               408.
## 2 2               406.
## 3 3               408.
## 4 4               450.
## 5 5               435.
## 6 6               439.
## 7 7               459.
## 8 8               446.
# add to bar plot with stat identity
ggplot(avg_total_gen, aes(x = generation, y = avg_total)) +
  geom_bar(stat = "identity")

The other approach is to use the stat_summary() function to perform the same plot. The difference from a normal bar plot is we again provide the y axis but provide the variable we want to aggregate, total in this case. We then call stat_summary() and add two arguments, the function we want to use and what type of geometry to use; we’ve used mean and bar.

While this is less code, which is a good thing, it is hard to understand the steps taken to make the summary.

ggplot(pokemon, aes(x = generation, y = total)) +
  stat_summary(fun = "mean", geom = "bar")

We can also add error bars to our plots to help us understand how precise our average measure is. To add error bars it is generally easier to use the group_by and summarise approach. We will look at two types of error bars, the standard deviation and the standard error of the mean.

The standard deviation indicates how close sample values are to the average of all data points, and the accuracy of the average. The standard error of the mean is the discrepancy of the sample mean and the true mean, telling you the accuracy of the sample mean.

To calculate, we do the same aggregation as we did before but add sd (standard deviation) to the summarise function and calculate the sem (standard error of the mean) in a mutate function.

# group and summarise to make average and sd per group
avg_total_gen <- pokemon %>%
  group_by(generation) %>%
  summarise(avg_total = mean(total, na.rm = TRUE),
            sd = sd(total, na.rm = TRUE)) %>%
  mutate(sem = sd/sqrt(length(sd)))

# print result
avg_total_gen
## # A tibble: 8 × 4
##   generation avg_total    sd   sem
##   <fct>          <dbl> <dbl> <dbl>
## 1 1               408.  99.9  35.3
## 2 2               406. 112.   39.7
## 3 3               408. 117.   41.2
## 4 4               450. 115.   40.7
## 5 5               435. 108.   38.2
## 6 6               439. 116.   40.9
## 7 7               459. 123.   43.6
## 8 8               446. 125.   44.3

To add error bars we use the geom_errorbar() function, which requires two arguments within an aes() function, the ymin and ymax. To find ymin or ymax we plus or minus our avg_total (y axis value) by the sd/sem.

# adding standard deviation error bars
ggplot(avg_total_gen, aes(x = generation, y = avg_total)) +
  geom_bar(stat = "identity") +
  geom_errorbar(aes(ymin = avg_total-sd, ymax = avg_total+sd)) +
  labs(title = "Average Pokemon total statistics by generation",
       subtitle = "Error bars indicate standard deviation")

# adding standard error bars
ggplot(avg_total_gen, aes(x = generation, y = avg_total)) +
  geom_bar(stat = "identity") +
  geom_errorbar(aes(ymin = avg_total-sem, ymax = avg_total+sem)) +
  labs(title = "Average Pokemon total statistics by generation",
       subtitle = "Error bars indicate standard error of the mean")

You can edit the look of the error bars, such as making them narrower and changing the colour. See the example below on how to do this. We’ve also changed the colour of the bars too.

ggplot(avg_total_gen, aes(x = generation, y = avg_total)) +
  geom_bar(stat = "identity", fill = "orange") +
  geom_errorbar(aes(ymin = avg_total-sem, ymax = avg_total+sem), width = 0.3, colour = "darkblue") +
  labs(title = "Average Pokemon total statistics by generation",
       subtitle = "Error bars indicate standard error of the mean")

If you want to add error bars to bar plots with different groupings on the x axis we need to made a few subtle changes, the main change is we need to have a dodge bar chart.

First we will re run our avg_total_gen aggregation and add another column to our group_by. We then pre-define how wide the bars and error bars should be. Instead of using position = "dodge" we use our dodge variable we just made, and add the fill to be legendary (our second grouping).

# group by legendary as well
avg_total_gen <- pokemon %>%
  group_by(generation, legendary) %>%
  summarise(avg_total = mean(total, na.rm = TRUE),
            sd = sd(total, na.rm = TRUE)) %>%
  mutate(sem = sd/sqrt(length(sd)))
## `summarise()` has grouped output by 'generation'. You can override using the
## `.groups` argument.
# pre-define the dodge position
dodge <- position_dodge(width = 0.8)

ggplot(avg_total_gen, aes(x = generation, y = avg_total, fill = legendary)) +
  geom_bar(stat = "identity", position = dodge) +
  geom_errorbar(aes(ymin = avg_total-sem, ymax = avg_total+sem), position = dodge, width = 0.3) +
  labs(title = "Average Pokemon total statistics by generation",
       subtitle = "Error bars indicate standard error of the mean") +
  scale_fill_manual(values = manual_pal)

Bar plots with other statistics exercise

Using the examples above and the Olympics dataset, make a visualisation of the average age (mean or median) of GBR (Great Britain) medal winners by medal type and gender, making sure to

  • show the difference between male and female athletes using colours
  • show error bars for either standard deviation or standard error of the mean
  • colour, label and save your visualisation

hint: don’t forgot to use dodge <- position_dodge(width = 0.8)

# your code here

Beyond bar plots

Bar plots are not the only option to view aggregated data, and there are some sources that suggest bar plots are less than ideal for any visualisation other than showing the frequency of a continuous variable. See https://paulvanderlaken.com/2018/12/17/avoid-bar-plots-for-continuous-data-do-this-instead/ for details on this.

Fortunately, there are alternatives, such as box plots which will be covered in the second data visualisation workshop, or we can use scatter plots! Scatter plots allow us to see all the data and we can add on an average, the best of both worlds.

In order to recreate what we just did with bar plots with scatter plots we can either use both geom_point() and stat_summary(), or make a summary table and add that using a second geom_point() function. First, lets just plot the data as a scatter plot, making the points larger and more transparent. Lowering the transparency (alpha) is important in these plots as darker colours indicate a higher density of data points.

ggplot(pokemon, aes(x = generation, y = total)) +
  geom_point(size = 5, alpha = .33)

Now we can add the stat_summary() function. We are going to use the mean, the geom is point, and the shape is a the - symbol (number 95); we will also make the shape larger so we can see it easier.

# using stat_summary
ggplot(pokemon, aes(x = generation, y = total)) +
  geom_point(size = 5, alpha = .33) +
  stat_summary(fun = mean, geom = "point",
               shape = 95, size = 20)

If we use the summary table option we first make a summary table with group_by() and summarise(). Then we add two geom_point() functions. The first has the pokemon data and our x and y axis. The second is our summary table, with the same x axis and the avg_total as the y axis.

# summary table option
gen_avg_total <- pokemon %>%
  group_by(generation) %>%
  summarise(avg_total = mean(total, na.rm = TRUE))

gen_avg_total
## # A tibble: 8 × 2
##   generation avg_total
##   <fct>          <dbl>
## 1 1               408.
## 2 2               406.
## 3 3               408.
## 4 4               450.
## 5 5               435.
## 6 6               439.
## 7 7               459.
## 8 8               446.
ggplot() +
  geom_point(data = pokemon,
             aes(x = generation, y = total),
             size = 5, alpha = .33) +
  geom_point(data = gen_avg_total,
             aes(x = generation, y = avg_total),
             shape = 95, size = 20)

Either option works well, but for the rest of the examples we will use the stat_summary() option as it is less code.

Now we have all our data so we can see the number of points for each group, and we can see the average per group!

Finally, we can add colour by our grouped variable (legendary) and change the colour palette. Just like with the bar plots we can adjust the positioning from stack to dodge. The examples below show both stack and dodge versions.

# position stacked
ggplot(pokemon, aes(x = generation, y = total, colour = legendary)) +
  geom_point(size = 5, alpha = 0.3) + 
  stat_summary(fun = mean, geom = "point",
               shape = 95, size = 20) +
  scale_colour_brewer(palette = "Set1")

# position dodge
dodge <- position_dodge(width = 0.8)

ggplot(pokemon, aes(x = generation, y = total, colour = legendary)) +
  geom_point(size = 5, alpha = 0.3, position = dodge) + 
  stat_summary(fun = mean, geom = "point",
               shape = 95, size = 20,
               position = dodge) +
  scale_colour_brewer(palette = "Set1")

Beyond bar plots exercise

Recreate your last visualisation, average age (mean or median) of GBR (Great Britain) medal winners by medal type and gender, using the geom_point() and stat_summary() method detailed above.

# your code here

Individual coding challenge

For the individual coding challenge we will be using the food consumption data from tidy Tuesday: https://github.com/rfordatascience/tidytuesday/blob/master/data/2020/2020-02-18/readme.md.

Use what we have covered in this workshop to make two visualisations of this dataset:

  • A scatter plot showing consumption and co2 emissions for a selected country (e.g. UK or France)
  • A bar plot of average co2 emissions per food category. Display just six countries to compare, such as UK, France, Germany etc. and colour them.

Use some of the tips we used and showed to make the visualisations have labels, colours and look appealing. Try and have some fun with it! =)

food_consumption <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-02-18/food_consumption.csv')
## Rows: 1430 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): country, food_category
## dbl (2): consumption, co2_emmission
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
food_consumption %>%
  glimpse()
## Rows: 1,430
## Columns: 4
## $ country       <chr> "Argentina", "Argentina", "Argentina", "Argentina", "Arg…
## $ food_category <chr> "Pork", "Poultry", "Beef", "Lamb & Goat", "Fish", "Eggs"…
## $ consumption   <dbl> 10.51, 38.66, 55.48, 1.56, 4.36, 11.39, 195.08, 103.11, …
## $ co2_emmission <dbl> 37.20, 41.53, 1712.00, 54.63, 6.96, 10.46, 277.87, 19.66…
# your code here

Understanding which visualisation to use and when

Sometimes it can be hard to know where to start with a visualisation. A great first starting point is understanding the options depending on the data types you have available. This website gives lots of information and visual guides on this process: https://www.data-to-viz.com/

Seeing what others have done with this data

The Olympics data we used for the exercises today is from the Tidy Tuesday GitHub repository. Tidy Tuesday is a social data visualisation challenge that happens every week and is a great way of learning about data viz.

The the link below to see what others have done and posted about using the Olympics data. Use it to get some ideas on what else you can try and do or get some inspiration from others. https://twitter.com/search?lang=en&q=%23tidytuesday%20olympics&src=typed_query

Fun extra

As a fun extra you can manually determine shapes in your visualisation using scale_shape_manual(). We’ve also removed the guide which was unnecessary by using guide = "none".

In the example below, as our x axis is generation from 1 to 8, we can make generation 1 have a shape of the number 1 and so on.

ggplot(pokemon, aes(x = generation, y = total, shape = generation)) +
  geom_point(size = 5, alpha = .33) +
  stat_summary(fun = mean, geom = "point",
               shape = 95, size = 20) +
  scale_shape_manual(values = c(49:56),
                     guide = "none")


R Data Visualistion 2 - Objective of workshop

To create histograms, box, and time series plots using the ggplot2 package.

What this workshop will cover

In this workshop, the aim is to cover how to work with dates in plots, and use histograms and box plots. We will be covering:

  • How to make box plots with ggplot2
  • Displaying distributions with histograms
  • Working with dates with the lubridate package
  • How to make time series line plots
  • How to split your data into facet grids

In this data visualisation workshop we will be building on the concepts learnt in the first workshop, constructing visualisations using the ggplot2 library.

We will be using one new package called lubridate, a tidyverse package which is designed to make working with dates and times easier; this will help us in making time series visualisations. Run the the code below to install lubridate.

# install lubridate
install.packages("lubridate")

Before we start we will need to load the libraries we will be using during this session. Run the code below to load your libraries.

# libraries we will be using
library(ggplot2)
library(dplyr)
library(lubridate)
library(readr)
library(janitor)
library(RColorBrewer)

Box plots

Box plots are designed to compare the differences of a categorical variable (samples or groups). They do this by displaying the summary statistics of a continuous variable (e.g. numeric) for each categorical variable.

The summary statistics shown are:

  • The median (middle value)
  • Interquartile range, known as IQR, which has values from 25% to 75% (or 25th to 75th percentile)
  • First quartile, known as Q1, which has a value of 25%
  • Second quartile, known as Q3, which has a value of 75%
  • “minimum” value, calculated as Q1 - 1.5*IQR
  • “maximum” value, calculated as Q3 + 1.5*IQR
  • Outlier, which are values that fall outside of the maximum or minimum values

We will use data from the Pokémon games again for our examples for box plots, which was web scraped from https://pokemondb.net/pokedex/all.

# load and clean names
pokemon <- read_csv("https://raw.githubusercontent.com/andrewmoles2/webScraping/main/R/data/pokemon.csv") %>%
  clean_names()
# review data
pokemon %>%
  glimpse()
## Rows: 952
## Columns: 13
## $ number     <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, …
## $ name       <chr> "Bulbasaur", "Ivysaur", "Venusaur", "Charmander", "Charmele…
## $ type1      <chr> "Grass", "Grass", "Grass", "Fire", "Fire", "Fire", "Water",…
## $ type2      <chr> "Poison", "Poison", "Poison", NA, NA, "Flying", NA, NA, NA,…
## $ total      <dbl> 318, 405, 525, 309, 405, 534, 314, 405, 530, 195, 205, 395,…
## $ hp         <dbl> 45, 60, 80, 39, 58, 78, 44, 59, 79, 45, 50, 60, 40, 45, 65,…
## $ attack     <dbl> 49, 62, 82, 52, 64, 84, 48, 63, 83, 30, 20, 45, 35, 25, 90,…
## $ defense    <dbl> 49, 63, 83, 43, 58, 78, 65, 80, 100, 35, 55, 50, 30, 50, 40…
## $ sp_atk     <dbl> 65, 80, 100, 60, 80, 109, 50, 65, 85, 20, 25, 90, 20, 25, 4…
## $ sp_def     <dbl> 65, 80, 100, 50, 65, 85, 64, 80, 105, 20, 25, 80, 20, 25, 8…
## $ speed      <dbl> 45, 60, 80, 65, 80, 100, 43, 58, 78, 45, 30, 70, 50, 35, 75…
## $ legendary  <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FAL…
## $ generation <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…

For these examples we will just look at one type of Pokémon, the electric type; the most famous of which is Pikachu! First, we extract just the electric type Pokémon, and make relevant columns factors.

# select columns to convert to factor
to_factor <- c("type1", "type2", "generation")

# extract just electric pokemon and make cols factors
electric_pokemon <- pokemon %>%
  filter(type1 == "Electric" | type2 == "Electric") %>%
  mutate(across(all_of(to_factor), factor))

head(electric_pokemon)
## # A tibble: 6 × 13
##   number name      type1    type2 total    hp attack defense sp_atk sp_def speed
##    <dbl> <chr>     <fct>    <fct> <dbl> <dbl>  <dbl>   <dbl>  <dbl>  <dbl> <dbl>
## 1     25 Pikachu   Electric <NA>    320    35     55      40     50     50    90
## 2     26 Raichu    Electric <NA>    485    60     90      55     90     80   110
## 3     81 Magnemite Electric Steel   325    25     35      70     95     55    45
## 4     82 Magneton  Electric Steel   465    50     60      95    120     70    70
## 5    100 Voltorb   Electric <NA>    330    40     30      50     55     55   100
## 6    101 Electrode Electric <NA>    490    60     50      70     80     80   150
## # … with 2 more variables: legendary <lgl>, generation <fct>

To make a box plot in ggplot we use the geom_boxplot() geom function. One of our axis variables has to be categorical and the other has to be numeric. In the below example we will use generation (categorical) and total (numeric).

# generation by total
ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot()

From the output we see a few things. First is that each box has a line through the middle which indicates the median; the box itself is our interquartile range. The lines above and below the boxes (known as whiskers) are the maximum and minimum values. The black dots indicate outliers, which have fallen outside our max and min values.

Just like with scatter and bar plots we can change the colours! You can use either fill or colour arguments with box plots, but fill tends to look better.

We will use the colour of Pikachu to colour our boxes. We used the pokemon colour picker to get the colour of pikachu: https://pokepalettes.com/#pikachu

ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot(fill = "#f6e652")

Sometimes it is useful to remove the outliers. To do so you add in the outlier.shape = NA argument.

ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot(fill = "#f6e652", outlier.shape = NA)

Displaying outliers is usually a good idea so we will keep them for now, and change the colour and shape of them. To adjust these we use outlier.colour and outlier.shape argments. We’ve used the colour of Pikachu’s cheeks as the outlier colour and made the shape square.

ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot(fill = "#f6e652", outlier.colour = "#c52018",
               outlier.shape = 15)

Box plots exercise

For the exercises for this workshops we will be using daily COVID data that is collected from most of the countries around the world.

COVID data is from our world in data, which is stored in a GitHub repository. More information on the data and what each variable means can be found here: https://github.com/owid/covid-19-data/tree/master/public/data

# load in covid data and select cases, deaths and vaccines
covid <- read_csv("https://covid.ourworldindata.org/data/owid-covid-data.csv") %>%
  select(iso_code:new_deaths_smoothed_per_million, contains("vaccin"),
         population, median_age, gdp_per_capita)
## Rows: 181452 Columns: 67
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr   (4): iso_code, continent, location, tests_units
## dbl  (62): total_cases, new_cases, new_cases_smoothed, total_deaths, new_dea...
## date  (1): date
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# have a quick look at the data
covid %>% glimpse()
## Rows: 181,452
## Columns: 30
## $ iso_code                                   <chr> "AFG", "AFG", "AFG", "AFG",…
## $ continent                                  <chr> "Asia", "Asia", "Asia", "As…
## $ location                                   <chr> "Afghanistan", "Afghanistan…
## $ date                                       <date> 2020-02-24, 2020-02-25, 20…
## $ total_cases                                <dbl> 5, 5, 5, 5, 5, 5, 5, 5, 5, …
## $ new_cases                                  <dbl> 5, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ new_cases_smoothed                         <dbl> NA, NA, NA, NA, NA, 0.714, …
## $ total_deaths                               <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_deaths                                 <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_deaths_smoothed                        <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ total_cases_per_million                    <dbl> 0.126, 0.126, 0.126, 0.126,…
## $ new_cases_per_million                      <dbl> 0.126, 0.000, 0.000, 0.000,…
## $ new_cases_smoothed_per_million             <dbl> NA, NA, NA, NA, NA, 0.018, …
## $ total_deaths_per_million                   <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_deaths_per_million                     <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_deaths_smoothed_per_million            <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ total_vaccinations                         <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ people_vaccinated                          <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ people_fully_vaccinated                    <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_vaccinations                           <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_vaccinations_smoothed                  <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ total_vaccinations_per_hundred             <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ people_vaccinated_per_hundred              <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ people_fully_vaccinated_per_hundred        <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_vaccinations_smoothed_per_million      <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_people_vaccinated_smoothed             <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_people_vaccinated_smoothed_per_hundred <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ population                                 <dbl> 39835428, 39835428, 3983542…
## $ median_age                                 <dbl> 18.6, 18.6, 18.6, 18.6, 18.…
## $ gdp_per_capita                             <dbl> 1803.987, 1803.987, 1803.98…

For this exercise will we make two box plots from our data looking more at the demographics of each continent (we will look at cases and vaccines later).

Your two box plots should show the following:

  • The median age of each continent
  • The gdp per capita for each continent
  • Make sure to change the colour of the boxes and outliers to make it look better!
  • Try changing the shape and size of your outlier

Hint: you will have to remove the na values from continent before plotting, e.g. covid %>% filter(!is.na(continent))

Hint: You can pipe from your filter function straight into ggplot2!

Hint: You can add colours in lots of ways but it can be fun to use a colour picker http://tristen.ca/hcl-picker/#/hlc/11/1.1/DC7261/D77357.

# your code here

Improving your box plots

The main issue with box plots, in a similar way to bar plots, is they can hide data. We can fix this by adding a scatter plot over the top of the boxes so we can see the full distribution of the data.

When adding in a scatter plot, we won’t need our outliers as the scatter plot will show these for us. We will need to remove them using the outlier.shape = NA argument.

ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot(fill = "#f6e652", outlier.shape = NA) +
  geom_point()

Some of our data points are overlapping which makes it a little hard to see all the data. We can fix this by changing the position of our points using the position = "jitter" argument. We can also use geom_jitter() which is a short hand for geom_point(position = "jitter"); we will use geom_jitter() going forward as it is less typing.

# change position in geom_point
ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot(fill = "#f6e652", outlier.shape = NA) +
  geom_point(position = "jitter")

# using geom_jitter
ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot(fill = "#f6e652", outlier.shape = NA) +
  geom_jitter()

We can also add in a colour grouping to our points to make them more meaningful. We add the colour aesthetic to our geom_jitter function. In the example we are colouring our points by if a pokemon is legendary or not.

ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot(fill = "#f6e652", outlier.shape = NA) +
  geom_jitter(aes(colour = legendary))

Finally we can change the colours of our points, which in this case we have done manually. Again, the colours were taken from the pokemon colour picker of pikachu: https://pokepalettes.com/#pikachu.

ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot(fill = "#f6e652", outlier.shape = NA) +
  geom_jitter(aes(colour = legendary)) +
  scale_colour_manual(values = c("#c52018", "#41414a"))

Now we can add a title and save the plot! When saving the plot we have manually adjusted the width of the plot. You can also change the height.

electric_pokemon_box <- ggplot(electric_pokemon, aes(x = generation, y = total)) +
  geom_boxplot(fill = "#f6e652", outlier.shape = NA) +
  geom_jitter(aes(colour = legendary)) +
  scale_colour_manual(values = c("#c52018", "#41414a")) +
  labs(title = "Summary of electric pokemon for each generation") +
  theme_bw()

electric_pokemon_box

ggsave("electric_pokemon_box.png", electric_pokemon_box,
       width = 5.5)
## Saving 5.5 x 5 in image

Improving your box plots exercise

For this exercise we will look at vaccines! We will look at 10 countries to see the difference in vaccine distribution; 5 have low gdp and 5 have high gdp. The data will be pre-prepared for you. We have made a vector with the counties that have high and low gdp. Then we have filtered our covid data by this vector, and made the location a ordered factor.

  1. Make a box plot using the covid_select_countries data, with x = location and y = total_vaccinations_per_hundred. Be sure to include geom_jitter().
  2. Now improve the look of your box plot! Change the colour of the boxes and the points, make the points more transparent, remove the outliers, change the theme, and flip the co-ordinates.
  3. Make another box plot the same way but use the people_fully_vaccinated variable as your y axis.
  4. Give both your box plots a title and change the axis labels (if you want).
  5. Save your plots using ggsave(). You will need to assign the plots to a variable first.
# Make vector with low and high gdp countries
high_low_gdp <- c("Sierra Leone", "Ethiopia","Yemen", 
                  "Zambia", "Nepal", "Sweden", "Australia",
                  "Saudi Arabia", "Germany", "United Kingdom")

# Only include locations in high_low_gdp
# Make location a factor, ordered by high_low_gdp
covid_select_countries <- covid %>%
  filter(location %in% high_low_gdp) %>%
  mutate(location = factor(location, levels = high_low_gdp))

# your code here

Displaying distributions with histograms

Histograms are great for visualising the distribution of numeric data. Histograms have one numerical variable as their input.

To make a histogram with ggplot we provide a numerical value to our x axis, and use the geom_histogram() geom. In the example we are using all the pokemon data and showing the distribution of the total column.

ggplot(pokemon, aes(x = total)) +
  geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

We can adjust the size of the bins of our plot with two methods, changing the binwidth or selecting the amount of bins. When we talk about bins with histograms it refers to the size of each bar; the larger the bar the more data on the x axis is included.

The first example uses binwidth. The number you provide is directly related to your x axis. In our example we are using the total column which goes up to 754. If we have binwidth = 8, then 8 data points will be included in each bin. Run the two examples below with a smaller and larger binwidth to see the results.

# summary stats for total column
summary(pokemon$total)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   175.0   325.0   450.0   429.5   505.0   754.0
# binwidth of 8
ggplot(pokemon, aes(x = total)) +
  geom_histogram(binwidth = 8) +
  labs(title = "Small binwidth (8)")

# binwidth of 50
ggplot(pokemon, aes(x = total)) +
  geom_histogram(binwidth = 50) +
  labs(title = "Larger binwidth (50)")

The other method is to select the number of bins to use, using the bins argument. The more bins we use, the less data will be contained in each bin. In the example below we have bins with lots of data bins = 10 and bins with less data bins = 50. Which do you think is best?

# using 10 bins
ggplot(pokemon, aes(x = total)) +
  geom_histogram(bins = 10) +
  labs(title = "Less bins = more data in each bin")

# using 50 bins
ggplot(pokemon, aes(x = total)) +
  geom_histogram(bins = 50) +
  labs(title = "More bins = less data in each bin")

It can be helpful to colour your histogram by a categorical variable. This works the same as a box plot, using the fill argument. In the example we have filled our histogram by the legendary category.

ggplot(pokemon, aes(x = total, fill = legendary)) +
  geom_histogram(binwidth = 20)

Another useful method is to use facets, which split up your data by a categorical variable and presents them in a grid like formation.

There are two techniques in ggplot to make facets, using facet_grid() or facet_wrap(). To use facet_grid() we define if we want to display our data row-wise (rows =) or column-wise (cols =). When defining which column to split our data by we need to use the vars() function. See the two examples below on how to do a row or column facet grid.

# row-wise display
ggplot(pokemon, aes(x = total, fill = legendary)) +
  geom_histogram(binwidth = 20) +
  facet_grid(rows = vars(legendary)) +
  labs(title = "Row-wise facet grid")

# column-wise display
ggplot(pokemon, aes(x = total, fill = legendary)) +
  geom_histogram(binwidth = 20) +
  facet_grid(cols = vars(legendary)) +
  labs(title = "column-wise facet grid")

The other option is facet_wrap(), which by default only needs the column you want to split your data by. It does allow extra specification with the nrow and ncol functions, allowing you to define how many rows and columns to display.

In the examples below we show the default facet_wrap, and how to adjust the column or row specification. We have used the generation column as it has more groups.

# default facet_wrap
ggplot(pokemon, aes(x = total, fill = legendary)) +
  geom_histogram(binwidth = 20) +
  facet_wrap(vars(generation)) +
  labs(title = "Default facet wrap")

# 4 rows
ggplot(pokemon, aes(x = total, fill = legendary)) +
  geom_histogram(binwidth = 20) +
  facet_wrap(vars(generation),
             nrow = 4) +
  labs(title = "Facet wrap with 4 rows")

# 4 columns
ggplot(pokemon, aes(x = total, fill = legendary)) +
  geom_histogram(binwidth = 20) +
  facet_wrap(vars(generation),
             ncol = 4) +
  labs(title = "Facet wrap with 4 columns")

Displaying distributions exercise

For this exercise we will be making a histogram of using the people_fully_vaccinated_per_hundred column for each continent

  • Make a histogram with people_fully_vaccinated_per_hundred as your x axis
  • Add a fill argment with continent
  • Adjust the binwidth or bins (e.g. binwidth = 5 looks good)
  • Using RColourBrewer, adjust the colours used in fill

Hint: you will have to remove the na values from continent before plotting, e.g. covid %>% filter(!is.na(continent))

Hint: You can pipe from your filter function straight into ggplot2!

Hint: To change the fill colours you can use scale_fill_brewer(palette = "a palette")

Hint: Use brewer.pal.info to find RColorBrewer palettes

# your code here

Working with the date data type with lubridate

Working with the date data type when programming can be a bit tricky for many reasons. There are different formats, time zones, and the challenge extracting information from the date. Fortunately, the lubridate package comes to the rescue!

There are three types of date data type: date (2010-09-01), time (15:08:52 BST), date-time (2010-09-01 15:08:52 BST). For this workshop we will be focusing on the date type as it is the most common.

You can find out today’s date (more useful than it sounds) or the date and time using the today() or now() functions.

# make sure dplyr and lubridate are loaded
library(dplyr)
library(lubridate)

# get today's date
today()
## [1] "2022-04-25"
# today's date and time
now()
## [1] "2022-04-25 16:14:25 BST"
# make today's date a variable
today_date <- today()

A great feature of lubridate is extracting the year, month, day, or week day information from your date. We can test it out on today’s date. Run the code to see how the output.

# year
year(today_date)
## [1] 2022
# month
month(today_date)
## [1] 4
month(today_date, label = TRUE)
## [1] Apr
## 12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec
# week
week(today_date)
## [1] 17
# day
day(today_date)
## [1] 25
# weekday
wday(today_date)
## [1] 2
wday(today_date, label = TRUE)
## [1] Mon
## Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat

Notice that for the month and wday functions we have the option to add labels. This can be very useful, making your month or week day outputs more readable.

For the rest of the examples we will use some randomised made up data containing daily sleep, and step information. Run the code below to see the data.

note: to make this data we have used randomisation functions: sample, runif and rnorm, if you are interested look them up to see how they work

# make some random data
df <- data.frame(
  date = seq(as.Date("2019-01-01"), as.Date("2021-12-01"), by = "days"),
  hours_sleep = round(rnorm(1066, mean = 9, sd = 1.5)),
  steps = round(rnorm(1066, mean = 8000, sd = 2000))
)

head(df)
##         date hours_sleep steps
## 1 2019-01-01           9  7084
## 2 2019-01-02           8  7518
## 3 2019-01-03           8  7281
## 4 2019-01-04           8  9719
## 5 2019-01-05           9  6302
## 6 2019-01-06          10  9760

We can now use the mutate function to make a year, month, week, day, and week day column.

df <- df %>%
  mutate(year = year(date),
         month = month(date, label = TRUE),
         week = week(date),
         day = day(date),
         week_day = wday(date, label = TRUE))

head(df)
##         date hours_sleep steps year month week day week_day
## 1 2019-01-01           9  7084 2019   Jan    1   1      Tue
## 2 2019-01-02           8  7518 2019   Jan    1   2      Wed
## 3 2019-01-03           8  7281 2019   Jan    1   3      Thu
## 4 2019-01-04           8  9719 2019   Jan    1   4      Fri
## 5 2019-01-05           9  6302 2019   Jan    1   5      Sat
## 6 2019-01-06          10  9760 2019   Jan    1   6      Sun
# see the breakdown of the date
df[1:2, c("date", "year", "month", "week", "day", "week_day")]
##         date year month week day week_day
## 1 2019-01-01 2019   Jan    1   1      Tue
## 2 2019-01-02 2019   Jan    1   2      Wed

Breaking the date down in this way allows us to do some aggregation of our data by the year, month, week, day, or weekday! In the examples below we have shown year and weekday.

# aggregate by year
df %>%
  group_by(year) %>%
  summarise(avg_sleep = mean(hours_sleep),
            avg_steps = mean(steps),
            total_steps = sum(steps))
## # A tibble: 3 × 4
##    year avg_sleep avg_steps total_steps
##   <dbl>     <dbl>     <dbl>       <dbl>
## 1  2019      9.19     8031.     2931381
## 2  2020      8.83     7897.     2890452
## 3  2021      8.92     8030.     2689951
# aggregate by week day
df %>%
  group_by(week_day) %>%
  summarise(avg_sleep = mean(hours_sleep),
            avg_steps = mean(steps),
            total_steps = sum(steps))
## # A tibble: 7 × 4
##   week_day avg_sleep avg_steps total_steps
##   <ord>        <dbl>     <dbl>       <dbl>
## 1 Sun           8.97     8004.     1216605
## 2 Mon           8.88     8045.     1222870
## 3 Tue           9.08     8063.     1233563
## 4 Wed           9.12     8122.     1242598
## 5 Thu           8.80     7824.     1189293
## 6 Fri           9.05     8004.     1216561
## 7 Sat           8.94     7831.     1190294

There are more functions from the lubridate package that we won’t be able to cover in this session, so do have a look at the package website for more information - https://lubridate.tidyverse.org/index.html - and checkout the R for Data Science chapter on dates - https://r4ds.had.co.nz/dates-and-times.html.

lubridate exercise

Using the examples above, extract year, month, day, day of week from covid data, and do an aggregation!

  1. Add new columns to your covid data for year, month, week, day and week_day. Try to add labels to month and week_day.
  2. Aggregate your covid data by year and month to find the mean total cases per million and mean total deaths per million.
  3. Print out the result.
# your code here

# separate date column
covid <- covid %>%
  mutate(year = year(date),
         month = month(date, label = TRUE),
         week = week(date),
         day = day(date),
         week_day = wday(date, label = TRUE))

# make year and month aggregate
avg_year_month_covid <- covid %>%
  group_by(year, month) %>%
  summarise(
    avg_total_cases_per_mil = mean(total_cases_per_million, na.rm = TRUE),
    avg_total_deaths_per_mil = mean(total_deaths_per_million, na.rm = TRUE)
    )
## `summarise()` has grouped output by 'year'. You can override using the
## `.groups` argument.
avg_year_month_covid
## # A tibble: 28 × 4
## # Groups:   year [3]
##     year month avg_total_cases_per_mil avg_total_deaths_per_mil
##    <dbl> <ord>                   <dbl>                    <dbl>
##  1  2020 Jan                     0.642                   0.0318
##  2  2020 Feb                     3.27                    0.325 
##  3  2020 Mar                   128.                      8.39  
##  4  2020 Apr                   606.                     34.0   
##  5  2020 May                  1078.                     58.9   
##  6  2020 Jun                  1654.                     74.3   
##  7  2020 Jul                  2414.                     92.9   
##  8  2020 Aug                  3434.                    114.    
##  9  2020 Sep                  4641.                    136.    
## 10  2020 Oct                  6508.                    161.    
## # … with 18 more rows

Time series plots

Time series plots visualise data over a period of time, which can be hourly, daily, weekly, monthly, or yearly. It is a great way to view trends over time. When plotting a time series, the x axis is the date and the y axis is your measure.

The most simple form of a time series visualisation in R is to use an unedited date variable. Using our example data (df) we will visualise how steps have changed each day.

# daily time series
df %>%
  ggplot(aes(x = date, y = steps)) +
  geom_line()

As we can see it is pretty variable how many steps are taken each day, as you might expect. There is a lot of data here so it is hard to see any real patterns, it just looks like noise! To solve this we can aggregate our data by the year, the month or the week to see if we can get any more insights.

For the example data we have it might be interesting to see the average of how many steps are taken on average each month, and to also compare this year on year.

We first aggregate our data, grouping by the month and year columns we made with the lubridate package, find the average steps, and convert the year column into a factor to make plotting easier; month is already a factor.

# aggregated time series by month
monthly_steps <- df %>%
  group_by(month, year) %>%
  summarise(avg_steps = mean(steps)) %>%
  mutate(year = factor(year))
## `summarise()` has grouped output by 'month'. You can override using the
## `.groups` argument.
monthly_steps
## # A tibble: 36 × 3
## # Groups:   month [12]
##    month year  avg_steps
##    <ord> <fct>     <dbl>
##  1 Jan   2019      7948.
##  2 Jan   2020      7574.
##  3 Jan   2021      7758.
##  4 Feb   2019      8619.
##  5 Feb   2020      7767.
##  6 Feb   2021      8146.
##  7 Mar   2019      8565.
##  8 Mar   2020      8108.
##  9 Mar   2021      8000.
## 10 Apr   2019      7361.
## # … with 26 more rows

Now we can make a time series by month! It is often helpful when using geom_line() to also pair it with geom_point() so we can see each data point clearly as well as seeing the trends with shown by the lines.

ggplot(monthly_steps,
       aes(x = month, y = avg_steps)) +
  geom_line() +
  geom_point()

That didn’t work as expected! As our data is grouped by year and month we need to use the group = argument to tell ggplot we want to connect the months up.

By adding group = year our plot will now look like a time series, run the code to check it out.

ggplot(monthly_steps,
       aes(x = month, y = avg_steps,
           group = year)) +
  geom_line() +
  geom_point()

It would also be helpful to see what year each line represents. We add the colour = year argument in as well to show this.

ggplot(monthly_steps,
       aes(x = month, y = avg_steps,
           group = year, colour = year)) +
  geom_line() +
  geom_point()

Our plot is still looking a little busy so we can use facets to split our data by year. We’ve used facet_wrap here with 3 rows.

ggplot(monthly_steps,
       aes(x = month, y = avg_steps,
           group = year, colour = year)) +
  geom_line() +
  geom_point() +
  facet_wrap(vars(year), nrow = 3)

Finally, we can make a few final adjustments and we have a nice visualisation that shows average step count per month for the year 2019 to 2021. Below is a list of all the additions make to change the look of the plot:

  • Changed the size of the lines and the points with the size = argument
  • Added a title and changed the axis names
  • Added a colour scale from the RColorBrewer package
  • Changed the theme to dark and changed the font to Avenir
  • Adjusted the y axis limits
step_count <- ggplot(monthly_steps,
       aes(x = month, y = avg_steps,
           group = year, colour = year)) +
  geom_line(size = 2.5) +
  geom_point(size = 3) +
  facet_wrap(vars(year), nrow = 3) +
  labs(title = "Average step count per month for the year 2019 to 2021",
       x = "Month", y = "Average steps (mean)",
       colour = "Year") +
  scale_colour_brewer(palette = "Pastel2") +
  theme_dark(base_family = "Avenir") +
  scale_y_continuous(limits = c(7000, 9000)) 

step_count
## Warning: Removed 1 row(s) containing missing values (geom_path).
## Warning: Removed 1 rows containing missing values (geom_point).

ggsave("step_count.png", step_count, width = 9)
## Saving 9 x 5 in image
## Warning: Removed 1 row(s) containing missing values (geom_path).
## Removed 1 rows containing missing values (geom_point).

Time series plots exercise

For this exercise we will be looking at the vaccine roll out for United Kingdom, India, Nepal, Israel, Germany, and Australia. Each country has had slightly different roll outs, with Israel being the fastest. We will be looking at the week by week roll out for 2021.

Data preparation:

  1. Make a vector called sel_country that includes United Kingdom, India, Nepal, Israel, Germany, and Australia
  2. Filter your covid data to include only locations that are in your sel_country vector, and filter for the year to be equal to 2021. Assign your filtered data to a variable called weekly_vax.
  3. Aggregate your weekly_vax data by week and location to find the mean of the people_vaccinated_per_hundred column. Assign the result back to weekly_vax
  4. Make the week and location columns of weekly_vax factors

Plotting:

Using your weekly_vax data you have just prepared:

  1. Make a time series plot with week as your x axis and your aggregation of the people_vaccinated_per_hundred column as your y axis.
  2. Colour and group your data by location.
  3. Make any aesthetic changes you think will make the plot better based on what we have covered so far, such as adding titles, changing colours, or adding facets (facet_grid() or facet_wrap()).
  4. Assign your final plot to a variable and save it!

Hint: if your x axis is looking squashed or cramped, try adding in scale_x_discrete(guide = guide_axis(n.dodge = 2))

# your code here

Final task - Please give us your individual feedback!

We would be grateful if you could take a minute before the end of the workshop so we can get your feedback!

https://lse.eu.qualtrics.com/jfe/form/SV_eflc2yj4pcryc62?coursename=R%20Data%20Visualisation%202:%20Box,%20histogram,%20and%20line%20plots&topic=R&link=https://lsecloud.sharepoint.com/:u:/s/TEAM_APD-DSL-Digital-Skills-Trainers/ERDaMePD5XBKgxuOMtN94YoB4aDZ5dxXqgPXBDdzWFxYSQ&prog=DS&version=21-22

The solutions we be available from a link at the end of the survey.

Individual coding challenge

For the coding challenge we will look at other things you can do with ggplot2 such as making artwork! This is known as generative art, which is produced either in part or completely by automated processes.

Generative art is a complex topic, but some of the ideas and styles can be done using the aRtsy package, https://koenderks.github.io/aRtsy/, which makes generative art more accessible.

First, you will need to install the aRtsy package.

# install aRtsy
install.packages("aRtsy")

Then you will need to load it!

# load aRtsy
library(aRtsy)

When making generative art it is a good idea to make it reproducible as we there is a lot of randomisation involved. When randomising in R you need to set a seed, which in simple terms means we reproduce our results using the same seed. We use the set.seed() function and add in any number. The number is our seed. If we gave someone else our code and our seed they would be able to reproduce or results.

We’ve given some examples below on making a striped artwork and flow fields. Run the code chunk below, then try changing the seed to see how the results change when you run it again!

Note: these will take a few moments to run!

# set the seed to 1
set.seed(1)

# make a colour palette from rcolorbrewer
set1 <- brewer.pal(n = 9, name = "Set1")
pastel1 <- brewer.pal(n = 9, name = "Pastel1")
paired <- brewer.pal(n = 12, name = "Paired")

# test out different parameters for stripes
canvas_stripes(paired, n = 800, H = 5, burnin = 5)

canvas_stripes(pastel1, n = 500, H = 15, burnin = 2)

# Test out different parameters for flow fields
canvas_flow(set1, background = "#fafafa", lines = 800, lwd = 0.30,
            iterations = 80, stepmax = 0.15)

pastel_flow <- canvas_flow(pastel1, background = "black", lines = 2000, lwd = 0.15,
            iterations = 30, stepmax = 0.10)

pastel_flow

# save pastel_flow
saveCanvas(pastel_flow, "pastel_flow.png")

Have a go yourself at making some generative art in R! Try out the following functions from aRtsy, changing the parameters to adjust the visualisation.

Don’t forget to save any of your artwork you like using the saveCanvas() function.

set.seed(1)

# your code here

Recommened resources to continue your data visualiation learning

The ggplot2 book is an excellent resource with lots of examples and exercises to have a go at https://ggplot2-book.org/.

Cedric Scherer writes blogs and tutorials on ggplot2 on his website. Some of his content is really great and worth looking through. Below are two of his tutorials to get you started:

Georgios Karamanis is a data visualisation designer and makes some amazing visualisations using R! It’s worth browsing his website for inspiration https://karaman.is/ or following him on twitter https://twitter.com/geokaramanis.

For ideas about what to do with your data have a look at the R graph gallery https://www.r-graph-gallery.com/.

LS0tCnRpdGxlOiAiUiBEYXRhIFZpc3VhbGlzYXRpb24gMSAmIDI6IEZhc3QtdHJhY2siCmF1dGhvcjoKICAgLSBuYW1lOiBBbmRyZXcgTW9sZXMKICAgICBhZmZpbGlhdGlvbjogTGVhcm5pbmcgRGV2ZWxvcGVyLCBEaWdpdGFsIFNraWxscyBMYWIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6IAogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICBrZWVwX21kOiBubwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IHRydWUKLS0tCgojIFIgRGF0YSBWaXN1YWxpc3Rpb24gMSAtIE9iamVjdGl2ZSBvZiB3b3Jrc2hvcAoKVG8gY3JlYXRlIHNjYXR0ZXIgYW5kIGJhciBwbG90IHZpc3VhbGlzYXRpb25zIHVzaW5nIHRoZSBnZ3Bsb3QyIHBhY2thZ2UuIAoKIyBXaGF0IHRoaXMgd29ya3Nob3Agd2lsbCBjb3ZlcgoKSW4gdGhpcyB3b3Jrc2hvcCwgdGhlIGFpbSBpcyB0byBjb3ZlciBob3cgdG8gdXNlIHRoZSBnZ3Bsb3QyIHBhY2thZ2UuIFdlIHdpbGwgYmUgY292ZXJpbmc6CgotICAgQW4gaW50cm9kdWN0aW9uIHRvIHRoZSBnZ3Bsb3QyIHBhY2thZ2UKLSAgIEhvdyB0byBtYWtlIHNjYXR0ZXIgcGxvdHMgd2l0aCBnZ3Bsb3QyCi0gICBIb3cgdG8gbWFrZSBiYXIgcGxvdHMgd2l0aCBnZ3Bsb3QyCi0gICBIb3cgdG8gY2hhbmdlIGNvbG91cnMgYW5kIG90aGVyIGZlYXR1cmVzIGluIHlvdXIgdmlzdWFsaXNhdGlvbnMgIAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEludHJvZHVjdGlvbgoKRGF0YSB2aXN1YWxpc2F0aW9uIGlzIGEgd2F5IG9mIGxvb2tpbmcgYXQgeW91ciBkYXRhIHVzaW5nIGdyYXBoaWNzLCB3aGljaCBwcm92aWRlcyBhIGRpZmZlcmVudCBwZXJzcGVjdGl2ZSB0byB5b3VyIGRhdGEuCgpUaGVyZSBhcmUgYSBsb3Qgb2YgZGlmZmVyZW50IG9wdGlvbnMgZm9yIGRhdGEgdmlzdWFsaXN0aW9uIHdpdGggUi4gWW91IGNhbiB1c2UgdGhlIHZpc3VhbGlzYXRpb24gdG9vbHMgdGhhdCBjb21lIHdpdGggUiwgZ2dwbG90IGFuZCBhbGwgaXRzIGV4dGVuc2lvbnMsIG9yIGZvciBpbnRlcmFjdGl2ZSB2aXN1YWxpc2F0aW9ucyB0aGVyZSBpcyB0aGUgcGxvdGx5IGxpYnJhcnkuCgohW10oaHR0cHM6Ly9naXRodWIuY29tL2FuZHJld21vbGVzMi9yVHJhaW5JbnRyb2R1Y3Rpb24vYmxvYi9tYWluL3ItZGF0YS12aXN1YWxpc2F0aW9uLTEvaW1hZ2VzL2dncGxvdDJfZXhwbG9yYXRvcnkucG5nP3Jhdz10cnVlKXt3aWR0aD0iNDM3In0KCkluIHRoaXMgZGF0YSB2aXN1YWxpc2F0aW9uIHNlcmllcyB3ZSB3aWxsIGJlIG1haW5seSBmb2N1c3Npbmcgb24gZ2dwbG90LCBhcyB3ZWxsIGFzIHBsb3RseS4gV2hpbGUgdGhlIHZpc3VhbGlzdGlvbiB0b29scyB0aGF0IGNvbWUgd2l0aCBSIGFyZSB1c2VmdWwsIGdncGxvdCBhbmQgcGxvdGx5IGFyZSBnZW5lcmFsbHkgZWFzaWVyIHRvIHVzZSBhbmQgbWFrZSBncmVhdCB2aXN1YWxpc2F0aW9ucyB3aXRoLiBGb3IgdGhpcyB0dXRvcmlhbCB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBiZWxvdyBwYWNrYWdlczogZ2dwbG90MiwgZHBseXIsIHJlYWRyLCBqYW5pdG9yLiBSdW4gdGhlIGNvZGUgYmVsb3cgdG8gaW5zdGFsbCB0aGUgcGFja2FnZXMgaWYgeW91IGRvbid0IGhhdmUgdGhlbSBpbnN0YWxsZWQgYWxyZWFkeS4KYGBge3IgZXZhbD1GQUxTRX0KIyBpbnN0YWxsIHBhY2thZ2VzCmluc3RhbGwgPC0gYygiZ2dwbG90MiIsICJkcGx5ciIsICJyZWFkciIsCiAgICAgICAgICAgICAiamFuaXRvciIsICJSQ29sb3JCcmV3ZXIiLAogICAgICAgICAgICAgImZvcmNhdHMiKQoKaW5zdGFsbC5wYWNrYWdlcyhpbnN0YWxsLCBOY3B1cyA9IDYpCmBgYAoKVGhlbiB3ZSBuZWVkIHRvIGxvYWQgdGhlbSBpbnRvIG91ciBzZXNzaW9uLiBSdW4gdGhlIGNvZGUgY2h1bmsgYmVsb3cgdG8gbG9hZCBhbGwgdGhlIGxpYnJhcmllcyB5b3Ugd2lsbCBuZWVkLgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojIGxvYWQgcGFja2FnZXMKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGZvcmNhdHMpCmBgYAoKIyBXaGF0IGlzIGdncGxvdCBhbmQgaG93IGRvZXMgaXQgd29yaz8KCmdncGxvdDIgaXMgYSBwYWNrYWdlIGZvciBwcm9kdWNpbmcgZ3JhcGhpY3MgdGhhdCB3b3JrcyBieSBjb21iaW5pbmcgaW5kZXBlbmRlbnQgY29tcG9uZW50cyB3aGVuIG1ha2luZyBncmFwaHMsIGtub3duIGFzIGxheWVycy4gVGhpcyBtYWtlcyBnZ3Bsb3QyIGJvdGggdmVyc2F0aWxlIGFuZCBwb3dlcmZ1bDsgeW91IGFyZSBub3QgbGltaXRlZCBieSBhIHNldCBvZiBvcHRpb25zIGJ1dCBpbnN0ZWFkIGNhbiBtYWtlIG5vdmVsIGdyYXBoaWNzIHRvIHN1aXQgeW91ciBuZWVkcy4KCkl0IGlzIGFsc28gaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBnZ3Bsb3QgY2FuIG9ubHkgdXNlIGRhdGEgZnJhbWVzLiBJZiB5b3VyIGRhdGEgaXMgaW4gYW5vdGhlciBmb3JtYXQgeW91IHdpbGwgbmVlZCB0byB0cmFuc2Zvcm0gaXQgaW50byBhIGRhdGEgZnJhbWUgaW4gb3JkZXIgdG8gdXNlIGdncGxvdC4KCkluIG9yZGVyIHRvIHVuZGVyc3RhbmQgaG93IHRoZSBsYXllcnMgd29yayB3ZSB3aWxsIGZpcnN0IGxvYWQgaW4gc29tZSBkYXRhIGZvciBvdXIgZXhhbXBsZXMuIFdlIHdpbGwgdXNlIGRhdGEgZnJvbSB0aGUgUG9rw6ltb24gZ2FtZXMsIHdoaWNoIHdhcyB3ZWIgc2NyYXBlZCBmcm9tIDxodHRwczovL3Bva2Vtb25kYi5uZXQvcG9rZWRleC9hbGw+LgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojIGxvYWQgYW5kIGNsZWFuIG5hbWVzCnBva2Vtb24gPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hbmRyZXdtb2xlczIvd2ViU2NyYXBpbmcvbWFpbi9SL2RhdGEvcG9rZW1vbi5jc3YiKSAlPiUKICBjbGVhbl9uYW1lcygpCiMgcmV2aWV3IGRhdGEKcG9rZW1vbiAlPiUKICBnbGltcHNlKCkKYGBgCgpUaGUgc3ludGF4IGZvciBnZ3Bsb3QgaGFzIHRocmVlIGtleSBjb21wb25lbnRzLiBUaGUgZ2dwbG90IGZ1bmN0aW9uIGNhbGwgKGBnZ3Bsb3QoKWApLCB0aGUgYWVzdGhldGljcyAoY2FsbGVkIGBhZXMoKWApLCBhbmQgdGhlIGdlb21ldHJ5IChjYWxsZWQgZ2VvbXMpIHdoaWNoIHJlZmVycyB0byBzY2F0dGVyLCBiYXIsIG9yIGxpbmUgcGxvdHMgZm9yIGV4YW1wbGUuIFRoZSBuZXh0IHRocmVlIGNvZGUgY2h1bmtzIGJyZWFrIHRoaXMgZG93bi4KCmBgYHtyfQojIGNhbGwgZ2dwbG90MiBhbmQgYWRkIGRhdGEKZ2dwbG90KHBva2Vtb24pCmBgYAoKTm90aWNlIHdlIGp1c3QgZ2V0IGEgZ3JleSBib3guIFdlIGhhdmUganVzdCBsb2FkZWQgb3VyIGRhdGEgaW50byBnZ3Bsb3QgYnV0IG5vdCBtdWNoIGVsc2UhIE5vdyBsZXRzIGFkZCB0aGUgYWVzdGhldGljcyBhbmQgc2VlIHdoYXQgaGFwcGVucy4KClRvbyBhZGQgYWVzdGhldGljcyB3ZSB1c2UgdGhlIGBhZXMoKWAgZnVuY3Rpb24gd2l0aGluIHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uLCBhbmQgc3BlY2lmeSB3aGF0IG91ciB4IGFuZCB5IGF4aXMgd2lsbCBiZSB3aXRoIGNvbHVtbiBuYW1lcyBmcm9tIG91ciBkYXRhLCBzcF9hdGsgYW5kIHNwX2RlZiBpbiB0aGlzIGNhc2UuCgpgYGB7cn0KIyBhZGQgYWVzdGhldGljcwpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSBzcF9hdGssIHkgPSBzcF9kZWYpKQpgYGAKCkl0IGlzIHN0YXJ0aW5nIHRvIGxvb2sgbW9yZSBsaWtlIGEgdmlzdWFsaXNhdGlvbiBub3csIHdlIGNhbiBzZWUgdGhlIHggYW5kIHkgYXhpcyBsYWJlbHMsIGJ1dCB3ZSBzdGlsbCBoYXZlIG5vIGRhdGEgcG9pbnRzIHNob3dpbmcuIFdlIGhhdmUgdG8gYWRkIGEgZ2VvbWV0cnkgZm9yIHRoYXQgdG8gaGFwcGVuLiBOb3RpY2UgdGhlIHN5bnRheCBoZXJlLCB3ZSB1c2UgdGhlIGArYCBpY29uIHRvIGFkZCBhIGdlb21ldHJ5IHRvIGdncGxvdCwgd2hpY2ggaW4gdGhpcyBjYXNlIGlzIGBnZW9tX3BvaW50KClgIHdoaWNoIG1ha2VzIHNjYXR0ZXIgcGxvdHMuIEFsbCBnZW9tZXRyeSBmdW5jdGlvbnMgc3RhcnQgd2l0aCBgZ2VvbV9gIGFuZCBlbmQgd2l0aCB0aGUgdHlwZSBvZiBnZW9tZXRyeSBzdWNoIGFzIHBvaW50LCBiYXIsIG9yIGxpbmUuCgpgYGB7cn0KIyBwaWNrIHdoaWNoIGdlb21ldHJ5CmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHNwX2F0aywgeSA9IHNwX2RlZikpICsKICBnZW9tX3BvaW50KCkKYGBgCgpUaGlzIGlzIHRoZSBmdW5kYW1lbnRhbCBjb25jZXB0IG9mIGdncGxvdCwgeW91IGNvbnN0cnVjdCB5b3VyIHZpc3VhbGlzYXRpb25zIGluIGxheWVycywgYWRkaW5nIGdlb21ldHJ5IGxheWVycywgYW5kIG90aGVyIGZlYXR1cmVzIGFzIHlvdSBnby4KCiMjIHdoYXQgaXMgZ2dwbG90IGV4ZXJjaXNlCgpVc2luZyB0aGUgcG9rZW1vbiBkYXRhLCBtYWtlIGEgc2NhdHRlciBwbG90IHdpdGggKmhwKiBvbiB0aGUgeCBheGlzIGFuZCAqc3BlZWQqIG9uIHRoZSB5IGF4aXMuCgpgYGB7cn0KIyB5b3VyIGNvZGUgaGVyZQoKYGBgCgojIFNjYXR0ZXIgcGxvdHMKClNjYXR0ZXIgcGxvdHMgYXJlIGZvciBkaXNwbGF5aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gbnVtZXJpYyAob3IgcXVhbnRpdGF0aXZlKSB2YXJpYWJsZXMuIEZvciBlYWNoIGRhdGEgcG9pbnQsIHRoZSB2YWx1ZXMgb2YgaXRzIGZpcnN0IHZhcmlhYmxlIGlzIHJlcHJlc2VudGVkIG9uIHRoZSBYIGF4aXMgYW5kIHRoZSBzZWNvbmQgb24gdGhlIFkgYXhpcy4KClRvIG1ha2UgYSBzY2F0dGVyIHBsb3Qgd2l0aCBnZ3Bsb3QyIHdlIHVzZSB0aGUgYGdlb21fcG9pbnQoKWAgZnVuY3Rpb24gbGlrZSB5b3UganVzdCBzYXcuIEluIG9yZGVyIGZvciBnZ3Bsb3QgdG8gbWFrZSBhIHNjYXR0ZXIgcGxvdCwgdGhlIFggYW5kIFkgYXhpcyBtdXN0IGJlIG51bWVyaWMuCgpUaGUgcGxvdCB3ZSBqdXN0IG1hZGUgaW4gdGhlIGV4YW1wbGUgaXMgb2theSBidXQgaXQgY291bGQgZG8gd2l0aCBzb21lIGltcHJvdmluZy4gVGhlcmUgYXJlIHF1aXRlIGEgZmV3IGRpZmZlcmVudCB3YXlzIHRvIGNoYW5nZSB0aGUgYXBwZWFyYW5jZSBvZiBhIHZpc3VhbGlzYXRpb24sIGxldHMgZ28gdGhyb3VnaCB0aGVtLgoKVGhlIGZpcnN0IHRoaW5nIHdlIHdpbGwgbG9vayBhdCBpcyBhZGRpbmcgc29tZSBjb2xvdXIhIFRoZXJlIGFyZSBhIGZldyBvcHRpb25zIGZvciBhZGRpbmcgY29sb3VycyB0byB5b3VyIHBsb3RzLiBZb3UgY2FuIGFkZCB0aGUgbmFtZSwgc3VjaCBhcyByZWQsIG9yIHlvdSBjYW4gdXNlIGEgaGV4IGNvZGUsIG9yIHlvdSBjYW4gdXNlIGEgcHJlLWRlZmluZWQgcGFsZXR0ZS4gVG8gYWRkIGNvbG91ciB0byBhIHNjYXR0ZXIgcGxvdCB3ZSB1c2UgdGhlIGBjb2xvdXIgPWAgYXJndW1lbnQuCgpgYGB7cn0KIyBjb2xvdXIgb2YgcG9pbnRzCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IGhwLCB5ID0gc3BlZWQpKSArCiAgZ2VvbV9wb2ludChjb2xvdXIgPSAib3JhbmdlIikKYGBgCgpUbyBjb2xvdXIgeW91ciBwb2ludHMgYnkgYSBncm91cCAob3IgZmFjdG9yKSB3ZSBoYXZlIHRvIGFkZCB0aGUgY29sb3VyIGFyZ3VtZW50IGludG8gdGhlIGBhZXMoKWAgZnVuY3Rpb24uIFRoaXMgYWxsb3dzIHVzIHRvIGhhdmUgZGlmZmVyZW50IGNvbG91cnMgZm9yIGRpZmZlcmVudCBncm91cHMsIHdoaWNoIG1ha2VzIHRoZSBwbG90IG1vcmUgaW5mb3JtYXRpdmUuCgpJbiB0aGUgYmVsb3cgZXhhbXBsZSwgb3VyIGRhdGEgaXMgY29sb3VyZWQgYnkgaWYgYSBwb2tlbW9uIGlzIGNsYXNzaWZpZWQgYXMgbGVnZW5kYXJ5IG9yIG5vdC4KCmBgYHtyfQojIGNvbG91ciBieSBmYWN0b3IKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gaHAsIHkgPSBzcGVlZCwgY29sb3VyID0gbGVnZW5kYXJ5KSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCldlIGdldCB0aGUgZGVmYXVsdCBnZ3Bsb3QgY29sb3VycyB3aGljaCBhcmUgb2theS4gVGhlcmUgYXJlIGEgZmV3IGRpZmZlcmVudCB3YXlzIG9mIGNoYW5naW5nIHRoZSBjb2xvdXJzLCBhbGwgbWV0aG9kcyB1c2UgdGhlIGBzY2FsZV9gIGZ1bmN0aW9uIGluIGEgc2xpZ2h0bHkgZGlmZmVyZW50IHdheS4gSW4gdGhlIHR3byBleGFtcGxlcyBiZWxvdyB3ZSBoYXZlIGNoYW5nZWQgdGhlIGNvbG91cnMgdXNpbmcgdGhlIFJDb2xvckJyZXdlciBwYWNrYWdlIGFuZCBoYXZlIHNldCB0aGUgY29sb3VycyBtYW51YWxseS4KClJDb2xvckJyZXdlciBjb21lcyB3aXRoIGEgc2V0IG9mIHBhbGV0dGVzIGZvciBkaWZmZXJlbnQgc2l0dWF0aW9ucywgeW91IGNhbiB2aWV3IHRoZW0gYnkgZm9sbG93aW5nIHRoaXMgbGluayA8aHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8zOC1yY29sb3JicmV3ZXJzLXBhbGV0dGVzLmh0bWw+LiBUbyB1c2UgdGhlc2UgcGFsZXR0ZXMgd2l0aCBnZ3Bsb3Qgd2UgdXNlIHRoZSBgc2NhbGVfY29sb3VyX2JyZXdlcigpYCBmdW5jdGlvbiB3aXRoIGFuIGFyZ3VtZW50IGZvciB3aGljaCBwYWxldHRlIHdlIHdhbnQgdG8gdXNlOyBpbiB0aGlzIGV4YW1wbGUgd2UgYXJlIHVzaW5nIFNldDEuCgpgYGB7cn0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCiMgYWRqdXN0aW5nIGNvbG91ciBieSBmYWN0b3IgdXNpbmcgUkNvbG9yQnJld2VyCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IGhwLCB5ID0gc3BlZWQsIGNvbG91ciA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKYGBgCgpUbyBtYWtlIGEgbWFudWFsIHBhbGV0dGUsIHlvdSBmaXJzdCBtYWtlIGEgdmVjdG9yIHdpdGggeW91ciBjb2xvdXJzLCB0byBkbyBzbyBpdCBpcyB1c2VmdWwgdG8gdXNlIGEgY29sb3VyIHBpY2tlciBzdWNoIGFzIDxodHRwOi8vdHJpc3Rlbi5jYS9oY2wtcGlja2VyLyMvaGxjLzYvMS8xNTUzNEMvRTJFMDYyPiBvciA8aHR0cHM6Ly9jb29sb3JzLmNvLz4uIFlvdSBjb3B5IHRoZSBoZXggY29kZSAoY29kZSB3aXRoIFwjIHRoZW4gNiBudW1iZXJzIG9mIGxldHRlcnMpIGFuZCBwYXN0ZSBpdCBpbnRvIHlvdXIgdmVjdG9yIGxpa2UgeW91IGNhbiBzZWUgaW4gdGhlIG1hbnVhbF9wYWwgdmVjdG9yIGJlbG93LiBUbyBhZGQgdGhlIGNvbG91ciB3ZSB1c2UgYHNjYWxlX2NvbG91cl9tYW51YWwoKWAgZnVuY3Rpb24sIGFuZCBzZXQgdGhlIHZhbHVlcyB0byBvdXIgbWFudWFsIHBhbGV0dGUuCgpgYGB7cn0KIyBhZGp1c3RpbmcgY29sb3VyIGJ5IGZhY3RvciB1c2luZyBtYW51YWwgcGFsZXR0ZQptYW51YWxfcGFsIDwtIGMoIiM5MEMwRjgiLCAiI0VBOTY0RSIpCgpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSBocCwgeSA9IHNwZWVkLCBjb2xvdXIgPSBsZWdlbmRhcnkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IG1hbnVhbF9wYWwpCmBgYAoKSXQgaXMgc29tZXRpbWVzIGhlbHBmdWwgdG8gdmlldyB0aGUgcGFsZXR0ZSBiZWZvcmUgdXNpbmcgaXQuIFdlIGNhbiB1c2UgdGhlIHNjYWxlcyBwYWNrYWdlIGZvciB0aGlzLCB3aGljaCBpcyBpbnN0YWxsZWQgd2hlbiB5b3UgaW5zdGFsbCBnZ3Bsb3QyLiBXZSBwcm92aWRlIHRoZSBgc2hvd19jb2woKWAgZnVuY3Rpb24gd2l0aCB0aGUgcGFsZXR0ZSB3ZSB3YW50IHRvIHZpZXcgYW5kIGl0IHJldHVybnMgYSBncmlkIHZpZXcgb2YgdGhlIGNvbG91cnMuIEluIHRoZSBleGFtcGxlIHdlIGxvb2sgYXQgU2V0MSBmcm9tIFJDb2xvckJyZXdlciBhbmQgdGhlIG1hbnVhbCBwYWxldHRlIHdlIGp1c3QgdXNlZC4KCmBgYHtyfQojIGxvYWQgc2NhbGVzCmxpYnJhcnkoc2NhbGVzKQojIHZpZXcgcGFsZXR0ZXMKc2hvd19jb2woUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG4gPSA4LCBuYW1lID0gIlNldDEiKSkKc2hvd19jb2wobWFudWFsX3BhbCkKYGBgCgpBcyB3ZWxsIGFzIGNoYW5naW5nIHRoZSBjb2xvdXIgb2YgdGhlIHBvaW50cywgeW91IGNhbiBjaGFuZ2UgdGhlaXIgc2hhcGUsIHNpemUsIGFuZCB0cmFuc3BhcmVuY3kgKGFscGhhKS4gSnVzdCBsaWtlIHdpdGggY29sb3VyLCB3ZSBjYW4gZGVmaW5lIHRoZSBzaXplLCBzaGFwZSBvciB0cmFuc3BhcmVuY3kgZWl0aGVyIGluIHRoZSBgYWVzKClgIGZ1bmN0aW9uIG9yIGluIGEgYGdlb21fYCBmdW5jdGlvbi4gQnkgYWRkaW5nIHRoZW0gdG8gdGhlIGBnZW9tX2AgZnVuY3Rpb24gd2UgbWFudWFsbHkgY2hhbmdlIHRoZW0uIElmIHdlIHVzZSB0aGVtIGluIGBhZXMoKWAgd2UgaGF2ZSB0byBhc3NvY2lhdGUgdGhlIHNpemUvc2hhcGUvYWxwaGEgd2l0aCBhIHZhcmlhYmxlLgoKU2VlIHRoZSBiZWxvdyBleGFtcGxlLCBmaXJzdCB3ZSBtYW51YWxseSBzZXQgdGhlIHNpemUgYW5kIGFscGhhLiBJbiB0aGUgc2Vjb25kIGV4YW1wbGUgd2Ugc2V0IHRoZSBzaXplIHRvIGJlIGRlZmluZWQgYnkgdGhlIHRvdGFsIGNvbHVtbiBpbiBvdXIgcG9rZW1vbiBkYXRhLCBhbmQgbWFudWFsbHkgc2V0IHRoZSBhbHBoYS4KCmBgYHtyfQojIG1hbnVhbGx5IHNldCBzaXplIGFuZCBhbHBoYQpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSBocCwgeSA9IHNwZWVkLCBjb2xvdXIgPSBsZWdlbmRhcnkpKSArCiAgZ2VvbV9wb2ludChzaXplID0gNSwgYWxwaGEgPSAwLjYpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpCgojIG1hbnVhbGx5IHNldCBhbHBoYSwgc2l6ZSBieSB0b3RhbApnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSBocCwgeSA9IHNwZWVkLCBjb2xvdXIgPSBsZWdlbmRhcnksIHNpemUgPSB0b3RhbCkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC42KSArCiAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQpgYGAKClRvIG1hbnVhbGx5IGNoYW5nZSB0aGUgc2hhcGUgYW5kIHJlcGxhY2UgdGhlIGNpcmNsZXMsIHdlIGdpdmUgdGhlIHNoYXBlIGFyZ3VtZW50IGEgbnVtYmVyLiBFYWNoIG51bWJlciByZXByZXNlbnRzIGEgc2hhcGUsIGxldHRlciwgb3IgbnVtYmVyOyBieSBkZWZhdWx0IGdncGxvdCB1c2VzIHNoYXBlIG51bWJlciAxOS4gV2UgY2FuIGNoYW5nZSB0aGUgc2hhcGUgdG8gYSBzcXVhcmUgZm9yIGV4YW1wbGUgYnkgdXNpbmcgdGhlIG51bWJlciAxNS4KCmBgYHtyfQojIGRlZmF1bHQgc2hhcGUgbnVtYmVyCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IGhwLCB5ID0gc3BlZWQsIGNvbG91ciA9IGxlZ2VuZGFyeSwgc2l6ZSA9IHRvdGFsKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYsIHNoYXBlID0gMTkpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpCgojIHNoYXBlIG51bWJlciBmb3Igc3F1YXJlcwpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSBocCwgeSA9IHNwZWVkLCBjb2xvdXIgPSBsZWdlbmRhcnksIHNpemUgPSB0b3RhbCkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC42LCBzaGFwZSA9IDE1KSArCiAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQpgYGAKClZpZXcgdGhlIGltYWdlIHdpdGggdGhlIHZpc3VhbCBtYXJrZG93biBlZGl0b3IgdG8gc2VlIHdoYXQgbnVtYmVyIHJlcHJlc2VudHMgd2hhdCBzaGFwZSwgbGV0dGVyLCBvciBudW1iZXIuCgohW10oaHR0cHM6Ly9naXRodWIuY29tL2FuZHJld21vbGVzMi9yVHJhaW5JbnRyb2R1Y3Rpb24vYmxvYi9tYWluL3ItZGF0YS12aXN1YWxpc2F0aW9uLTEvaW1hZ2VzL3NoYXBlcy5wbmc/cmF3PXRydWUpe3dpZHRoPSI4MDAiIGhlaWdodD0iOTAwIn0KCkZpbmFsbHkgd2UgY2FuIGFkZCBhIHRpdGxlIGFuZCBzYXZlIG91ciBwbG90ISBXZSd2ZSBkb25lIHR3byB0aGluZ3MgaW4gb3JkZXIgdG8gYWNoaWV2ZSB0aGlzLiBUbyBhZGQgYSB0aXRsZSwgYW5kIGNoYW5nZSBheGlzIGxhYmVscywgd2UgaGF2ZSB1c2VkIHRoZSBgbGFicygpYCBmdW5jdGlvbi4gV2UgYWRkIGFyZ3VtZW50cyBmb3Igd2hhdCB3ZSB3YW50IHRvIGNoYW5nZSwgc3VjaCBhcyBgdGl0bGUgPSAiUG9rZW1vbiBIaXQgUG9pbnRzIHZzIFNwZWVkImAuIFRvIGNoYW5nZSB0aGUgbGVnZW5kIGxhYmVscyB3ZSB1c2UgY29sb3VyIGFuZCBzaXplLCBhcyB3ZSB1c2VkIHRoZXNlIHRvIGRlZmluZSBvdXIgbGVnZW5kIGluIHRoZSBgYWVzKClgIGZ1bmN0aW9uLgoKVG8gc2F2ZSB0aGUgcGxvdCB3ZSBhc3NpZ24gb3VyIGNvZGUgdG8gYSB2YXJpYWJsZSwgdGhlbiB3ZSB1c2UgdGhlIGBnZ3NhdmUoKWAgZnVuY3Rpb24sIHdoaWNoIHJlcXVpcmVzIHdoYXQgeW91IHdhbnQgdG8gY2FsbCB0aGUgZmlsZSBhbmQgdGhlIGZpbGUgZXh0ZW5zaW9uIChlLmcuIHBsb3QuUE5HIG9yIHBsb3QuSlBHKSwgdGhlbiB0aGUgZ2dwbG90IG9iamVjdCB3ZSBjcmVhdGVkLiBSdW4gdGhlIGV4YW1wbGUgYmVsb3csIGFuZCB5b3Ugc2hvdWxkIGdldCBhIGhwX3ZzX3NwZWVkLlBORyBmaWxlIHdoZXJlIHlvdXIgUm1kIGZpbGUgaXMgc2F2ZWQuIFlvdSBjYW4gYWxzbyBhZGp1c3QgdGhlIHNpemUgb2YgdGhlIGltYWdlIHNhdmVkIHVzaW5nIHRoZSB3aWR0aCBhbmQgaGVpZ2h0IGFyZ3VtZW50cy4KCmBgYHtyfQojIHNhdmUgcGxvdCB0byBhIHZhcmlhYmxlCmhwX3ZzX3NwZWVkIDwtIGdncGxvdChwb2tlbW9uLCBhZXMoeCA9IGhwLCB5ID0gc3BlZWQsIGNvbG91ciA9IGxlZ2VuZGFyeSwgc2l6ZSA9IHRvdGFsKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYsIHNoYXBlID0gMTUpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICsKICBsYWJzKHRpdGxlID0gIlBva2Vtb24gSGl0IFBvaW50cyB2cyBTcGVlZCIsCiAgICAgICBzdWJ0aXRsZSA9ICJUYWtlbiBmcm9tIHBva2Vtb25kYi5uZXQiLAogICAgICAgeCA9ICJIaXQgUG9pbnRzIiwKICAgICAgIHkgPSAiU3BlZWQiLAogICAgICAgY29sb3VyID0gIkxlZ2VuYXJ5IHBva2Vtb24/IiwKICAgICAgIHNpemUgPSAiVG90YWwgc3RhdHMiKQoKaHBfdnNfc3BlZWQKCiMgc2F2ZSBwbG90Cmdnc2F2ZSgiaHBfdnNfc3BlZWQuUE5HIiwgaHBfdnNfc3BlZWQpCgojIHNhdmUgd2l0aCBkZWZpbmVkIHdpZHRoIGFuZCBoZWlnaHQKZ2dzYXZlKCJocF92c19zcGVlZC5QTkciLCBocF92c19zcGVlZCwKICAgICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNC41KQpgYGAKCiMjIFNjYXR0ZXIgcGxvdHMgZXhlcmNpc2UKCkZvciB0aGUgZXhlcmNpc2VzIGluIHRoaXMgd29ya3Nob3Agd2Ugd2lsbCB1c2UgZGF0YSBmcm9tIHRoZSBPbHltcGljcyB0aGF0IGluY2x1ZGVzIGFsbCBPbHltcGljIGdhbWVzIGZyb20gMTg5NiB0aHJvdWdoIHRvIDIwMTYuIE1vcmUgaW5mb3JtYXRpb24gb24gdGhlIGRhdGFzZXQgY2FuIGJlIGZvdW5kIGhlcmUgPGh0dHBzOi8vZ2l0aHViLmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvYmxvYi9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDctMjcvcmVhZG1lLm1kPi4gUnVuIHRoZSBjb2RlIHByb3ZpZGVkIHRvIGxvYWQgdGhlIGxpYnJhcmllcyBhbmQgZGF0YSBpbnRvIFIuCgpXZSB3aWxsIG1ha2UgdHdvIHNjYXR0ZXIgcGxvdHMgZnJvbSB0aGUgT2x5bXBpY3MgZGF0YS4gRm9yIGJvdGggcGxvdHMgd2Ugd2lsbCB1c2UgZHBseXIgdG8gZmlsdGVyIHRoZSBpbmZvcm1hdGlvbiB3ZSBhcmUgaW50ZXJlc3RlZCBpbiwgd2hpY2ggaGFzIGJlZW4gZG9uZSBmb3IgeW91IGluIHRoaXMgZXhlcmNpc2UuIAoKMSkgIFVzaW5nIHRoZSBwcm92aWRlZCBgc2NhdHRlcl9wbG90MWAgZGF0YSwgbWFrZSBhIHNjYXR0ZXIgcGxvdCBvZiBPbHltcGljIGd5bW5hc3RzIGhlaWdodHMgKHggYXhpcykgYW5kIHdlaWdodHMgKHkgYXhpcykuCgotICAgQ2hhbmdlIHRoZSBjb2xvdXIgYW5kIHNoYXBlIGFyZ3VtZW50cyB0byB0ZWxsIHVzIHdoYXQgc2V4IHRoZSBneW1uYXN0cyBhcmUuCi0gICBDaGFuZ2UgdGhlIGNvbG91ciBwYWxldHRlIGJ5IG1ha2luZyBhIG1hbnVhbCBvbmUgb3IgdXNpbmcgUkNvbG9yQnJld2VyLgotICAgQmUgc3VyZSB0byBnaXZlIHlvdXIgcGxvdCBhIHRpdGxlLCBhbmQgc2F2ZSB5b3VyIHBsb3QuCgoyKSAgVXNpbmcgdGhlIHByb3ZpZGVkIGBzY2F0dGVyX3Bsb3QyYCBkYXRhLCBtYWtlIGEgc2NhdHRlciBwbG90IG9mIHRoZSBhZ2UgKHkgYXhpcykgb2YgZ3ltbmFzdGljIG1lZGFsIHdpbm5lcnMgYnkgeWVhciAoeCBheGlzKS4gCgotICAgQ29sb3VyIHlvdXIgcGxvdCBieSBtZWRhbCBieSBtYWtpbmcgYSBtYW51YWwgY29sb3VyIHBhbGV0dGUuICpoaW50OiB0aGUgaGV4IGNvZGVzIGZvciBnb2xkLCBzaWx2ZXIgYW5kIGJyb256ZSBhcmU6ICIjRkZENzAwIiwgIiNDMEMwQzAiLCAiI0NEN0YzMiIqCi0gICBVc2Ugc2hhcGUgdG8gdGVsbCB1cyB3aGF0IHNleCB0aGUgZ3ltbmFzdHMgd2VyZS4KLSAgIEJlIHN1cmUgdG8gZ2l2ZSB5b3VyIHBsb3QgYSB0aXRsZSwgYW5kIHNhdmUgeW91ciBwbG90LgoKCmBgYHtyfQojIG1ha2Ugc3VyZSBsaWJyYXJpZXMgYXJlIGxvYWRlZApsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQoKIyBsb2FkIGluIGRhdGEKb2x5bXBpY3MgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAyMS8yMDIxLTA3LTI3L29seW1waWNzLmNzdiIpCgpvbHltcGljcyAlPiUgZ2xpbXBzZSgpCgojIGRhdGEgY2xlYW5pbmcgZm9yIGZpcnN0IHNjYXR0ZXIgcGxvdApzY2F0dGVyX3Bsb3QxIDwtIG9seW1waWNzICU+JQogIGZpbHRlcihzcG9ydCA9PSAiR3ltbmFzdGljcyIpCgojIGRhdGEgY2xlYW5pbmcgZm9yIHNlY29uZCBzY2F0dGVyIHBsb3QKc2NhdHRlcl9wbG90MiA8LSBvbHltcGljcyAlPiUKICBmaWx0ZXIoc3BvcnQgPT0gIkd5bW5hc3RpY3MiKSAlPiUKICBmaWx0ZXIoIWlzLm5hKG1lZGFsKSkgJT4lCiAgbXV0YXRlKG1lZGFsID0gZmFjdG9yKG1lZGFsLCBsZXZlbHMgPSBjKCJHb2xkIiwgIlNpbHZlciIsICJCcm9uemUiKSkpCgojIHlvdXIgY29kZSBoZXJlCgpgYGAKCiMgUXVpcmtzIG9mIGdncGxvdDIKClRoZXJlIGFyZSBhIGZldyBxdWlya3MgdG8gYmUgYXdhcmUgb2Ygd2hlbiB1c2luZyBnZ3Bsb3QyIGFuZCB5b3UnbGwgc2VlIGEgZmV3IG9mIHRoZW0gd2hlbiB5b3UgbG9vayBmb3IgZXhhbXBsZXMgb25saW5lLiBJbiBvcmRlciB0byBhaWQgd2l0aCB0aGlzLCB3ZSBjYW4gaGF2ZSBhIGxvb2sgYXQgYSBmZXcgb2YgdGhlbSEKClRoZSBmaXJzdCBxdWlyayBpcyBwaXBpbmcgZGF0YSBpbnRvIGdncGxvdCwgd2hlcmUgeW91IGRvIG5vdCBuZWVkIHRvIGFkZCB5b3VyIGRhdGEgaW50byB0aGUgYGdncGxvdCgpYCBmdW5jdGlvbiBhcyBpdCBpcyBwaXBlZCBpbi4gVGhlIG1haW4gYWR2YW50YWdlIG9mIHRoaXMgYXBwcm9hY2ggaXMgeW91IGNhbiBzdHJpbmcgdG9nZXRoZXIgc29tZSBkYXRhIGNsZWFuaW5nIGFuZCB0aGVuIHBpcGUgdGhlIHJlc3VsdHMgc3RyYWlnaHQgaW50byBnZ3Bsb3QuCgpgYGB7cn0KIyBwaXBpbmcgZGF0YSBpbnRvIGdncGxvdApwb2tlbW9uICU+JQogIGdncGxvdChhZXMoeCA9IHNwX2F0aywgeSA9IHNwX2RlZikpICsKICBnZW9tX3BvaW50KCkKCiMgcGlwaW5nIHdpdGggZmlsdGVyCnBva2Vtb24gJT4lCiAgZmlsdGVyKHR5cGUxID09ICJGaXJlIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc3BfYXRrLCB5ID0gc3BfZGVmKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKClRoZSBzZWNvbmQgcXVpcmsgaXMgYWRkaW5nIGFlc3RoZXRpY3MgaW50byBhIGBnZW9tX2AgZnVuY3Rpb24gcmF0aGVyIHRoYW4gdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24uCgpgYGB7cn0KIyBhZGRpbmcgYWVzdGhldGljcyBpbnRvIHRoZSBnZW9tXyBjYWxsCmdncGxvdChwb2tlbW9uKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHNwX2F0aywgeSA9IHNwX2RlZikpCmBgYAoKVGhlIHRoaXJkIHF1aXJrIGlzIHlvdSBjYW4gYWxzbyBhZGQgdGhlIGRhdGEgaW50byB0aGUgYGdlb21fYCBmdW5jdGlvbi4gV2hlbiBkb2luZyBzbyB5b3UgaGF2ZSB0byBoYXZlIGBkYXRhID1gIG90aGVyd2lzZSB5b3Ugd2lsbCBnZXQgYW4gZXJyb3IuCgpgYGB7cn0KIyBhZGRpbmcgZGF0YSBhbmQgYWVzdGhldGljcyBpbnRvIHRoZSBnZW9tXyBjYWxsCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwb2tlbW9uLCBhZXMoeCA9IHNwX2F0aywgeSA9IHNwX2RlZikpCmBgYAoKVGhlIGZvdXJ0aCBxdWlyayByZWxhdGVzIHRvIHRoZSBzZWNvbmQgYW5kIHRoaXJkLCBpbiB0aGF0IHlvdSBjYW4gYWRkIGFlc3RoZXRpY3MgaW50byBhIGBnZW9tX2AgZnVuY3Rpb24gbW9yZSB0aGFuIG9uY2UuIFlvdSBtaWdodCBvY2Nhc2lvbmFsbHkgY29tZSBhY3Jvc3MgdGhpcyBmb3IgbW9yZSBjb21wbGV4IHZpc3VhbGlzYXRpb25zLgoKSW4gdGhlIGV4YW1wbGUgd2Ugd2lsbCBhZGQgdGhlIGF2ZXJhZ2Ugb2Ygb3VyIHggYW5kIHkgdmFyaWFibGVzLiBGaXJzdCB3ZSBtYWtlIGEgc3VtbWFyeSB0YWJsZSB0aGF0IGhhcyB0aGUgYXZlcmFnZXMgb2YgYm90aCBheGlzJ3MsIHVzaW5nIGBzdW1tYXJpc2UoKWAgZnJvbSBkcGx5ci4gVGhlbiB3ZSBhZGQgdHdvIGBnZW9tX3BvaW50KClgIGZ1bmN0aW9ucywgb25lIHdpdGggdGhlIHBva2Vtb24gZGF0YSwgYW5kIG9uZSB3aXRoIG91ciBzdW1tYXJ5IHRhYmxlIGRhdGEuCgpgYGB7cn0KIyB3aHkgYWRkaW5nIGFlc3RoZXRpY3MgaW50byB0aGUgZ2VvbV8gY2FsbAojIGNhbGN1bGF0ZSBtZWFuIG9mIHNwX2F0ayBhbmQgc3BfZGVmCmF2Z19zcCA8LSBwb2tlbW9uICU+JQogIHN1bW1hcmlzZSgKICAgIGF2Z19zcF9hdGsgPSBtZWFuKHNwX2F0aywgbmEucm0gPSBUUlVFKSwKICAgIGF2Z19zcF9kZWYgPSBtZWFuKHNwX2RlZiwgbmEucm0gPSBUUlVFKSkKCmF2Z19zcAoKIyBhZGQgYXZlcmFnZSBzcF9hdGsgYW5kIHNwX2RlZiBhcyBibGFjayBwb2ludApnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcG9rZW1vbiwgCiAgICAgICAgICAgICBhZXMoeCA9IHNwX2F0aywgeSA9IHNwX2RlZiksIAogICAgICAgICAgICAgY29sb3VyID0gIm9yYW5nZSIsCiAgICAgICAgICAgICBzaXplID0gMi41KSArCiAgZ2VvbV9wb2ludChkYXRhID0gYXZnX3NwLCAKICAgICAgICAgICAgIGFlcyh4ID0gYXZnX3NwX2F0aywgeSA9IGF2Z19zcF9kZWYpLAogICAgICAgICAgICAgc2l6ZSA9IDIuNSkKYGBgCgpUaGUgbGFzdCBxdWlyayB3ZSB3aWxsIGxvb2sgYXQgaXMgYWRkaW5nIHRvIGEgZ2dwbG90IHZpc3VhbGlzYXRpb24gYWZ0ZXIgeW91IGhhdmUgYXNzaWduZWQgaXQgYSBuYW1lLiBUaGlzIGlzIHZlcnkgY29tbW9uIGluIHR1dG9yaWFscyBhbmQgb24gU3RhY2sgT3ZlcmZsb3cuIEEgZ29vZCB1c2Ugb2YgdGhpcyBpcyB0byBidWlsZCBhIGJhc2Ugb2YgdGhlIHggYW5kIHkgeW91IHdhbnQgdG8gdXNlIGFuZCB0ZXN0IG91dCBkaWZmZXJlbnQgZ2VvbWV0cmllcy4KCmBgYHtyfQojIHNhdmluZyBwbG90IHRoZW4gYWRkaW5nIHRvIGl0CnAgPC0gZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gc3BfYXRrLCB5ID0gc3BfZGVmKSkKCnAKCnAgKyBnZW9tX3BvaW50KCkKCnAgKyBnZW9tX2xpbmUoKQpgYGAKCiMgUXVpcmtzIG9mIGdncGxvdDIgZXhlcmNpc2UKCk1ha2UgYSB2aXN1YWxpc2F0aW9uIG9mIFVTQSBhdGhsZXRlcyBhZ2VzIHZzIGhlaWdodHMsIHNob3dpbmcgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgZ2VuZGVycyB1c2luZyBjb2xvdXIuIFdoZW4gbWFraW5nIHlvdXIgdmlzdWFsaXNhdGlvbiB0cnkgdG8KCi0gICBQaXBlIHRoZSBvbHltcGljcyBkYXRhIHRvIGEgZmlsdGVyIGZ1bmN0aW9uIGFuZCBzZWxlY3QgYWxsIFVTQSBhdGhsZXRlcwotICAgUGlwZSB0byBhIGdncGxvdCBmdW5jdGlvbgotICAgQWRkIGEgZ2VvbV9wb2ludCBmdW5jdGlvbiBhbmQgYWRkIHRoZSBhZXN0aGV0aWNzIHRoZXJlIHJhdGhlciB0aGFuIGluIGBnZ3Bsb3QoKWAKCmBgYHtyfQojIHlvdXIgY29kZSBoZXJlCgpgYGAKCiMgQmFyIHBsb3RzIHdpdGggY291bnRzCgpCYXIgcGxvdHMgYXJlIHVzZWQgdG8gc2hvdyByZWxhdGlvbnNoaXBzIGJldHdlZW4gYSBudW1lcmljYWwgYW5kIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBUaGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgaXMgdXN1YWxseSBvbiB0aGUgeCBheGlzLCBhbmQgdGhlIHkgYXhpcyBpcyB1c3VhbGx5IGEgZnJlcXVlbmN5IGNvdW50LgoKQnkgZGVmYXVsdCwgYmFyIHBsb3RzIHdpdGggZ2dwbG90IG9ubHkgcmVxdWlyZSBhbiB4IG9yIHkgYXhpcy4gRnJvbSB0aGF0IHRoZXkgbWFrZSBhIGZyZXF1ZW5jeSBjb3VudCBvZiB0aGF0IHZhcmlhYmxlLiBTZWUgdGhlIGV4YW1wbGUgYmVsb3cuIEZpcnN0IHdlIHVzZSBnZ3Bsb3QgdG8gbWFrZSBhIGJhciBwbG90IHRvIGNvdW50IHRoZSBudW1iZXIgb2YgcG9rZW1vbiBhZGRlZCBpbiBlYWNoIGdlbmVyYXRpb24uIFRoZW4gd2UgZG8gdGhlIHNhbWUgdGhpbmcgd2l0aCBkcGx5ciB0byBtYWtlIGEgYWdncmVnYXRlIHRhYmxlLCBnZ3Bsb3QgaXMgdGFraW5nIHRoaXMgYWdncmVnYXRlIHRhYmxlIGFuZCBtYWtpbmcgaW50byBhIHBsb3QgZm9yIHVzIQoKSXQgaXMgaW1wb3J0YW50IHRvIG1ha2Ugc3VyZSB5b3VyIHggYXhpcyBpbiBhIGJhciBwbG90IGlzIGEgZmFjdG9yLCBhcyB0aGlzIGhlbHBzIGdncGxvdCB0byBvcmRlciB0aGUgYXhpcyBhcyB5b3UgZXhwZWN0LgoKYGBge3J9CiMgbWFrZSBnZW5lcmF0aW9uIGEgZmFjdG9yCnBva2Vtb24kZ2VuZXJhdGlvbiA8LSBmYWN0b3IocG9rZW1vbiRnZW5lcmF0aW9uKQoKIyBkZWZhdWx0IGJhciBwbG90CmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24pKSArCiAgZ2VvbV9iYXIoKQoKIyBkcGx5ciBhZ2dyZWdhdGUgZXF1aXZhbGVudApwb2tlbW9uICU+JQogIGNvdW50KGdlbmVyYXRpb24pCmBgYAoKVG8gYWRkIGNvbG91ciB0byB5b3VyIGJhciBwbG90IHdlIHVzZSB0aGUgZmlsbCBhcmd1bWVudCByYXRoZXIgdGhhbiBjb2xvdXIuIFRoaXMgY2FuIGJlIGNvbmZ1c2luZywgYW5kIHNvbWV0aW1lcyBpZiB5b3UgZm9yZ2V0LCBqdXN0IHRyeSBib3RoIHRpbGwgdGhlIGNvbG91cnMgbG9vayByaWdodCEgVG8gYWRkIG91ciBmaWxsIG1hbnVhbGx5IHdlIGFkZCB0aGUgZmlsbCBjb21tYW5kIHRvIG91ciBgZ2VvbV9iYXIoKWAgZnVuY3Rpb24uCgpgYGB7cn0KIyBtYW51YWxseSBhZGQgZmlsbCBjb2xvdXIKZ2dwbG90KHBva2Vtb24sIGFlcyhnZW5lcmF0aW9uKSkgKwogIGdlb21fYmFyKGZpbGwgPSAicHVycGxlIikKYGBgCgpKdXN0IGxpa2Ugd2l0aCB0aGUgc2NhdHRlciBwbG90LCB3ZSBjYW4gY29sb3VyIG91ciBwbG90IGJ5IGEgdmFyaWFibGUgYnkgcHV0dGluZyB0aGUgZmlsbCBhcmd1bWVudCB3aXRoaW4gdGhlIGBhZXMoKWAgZnVuY3Rpb24uIFRoZSBiZWxvdyBleGFtcGxlIGFsc28gc2hvd3MgdGhlIGVxdWl2YWxlbnQgd2hlbiBkb2luZyBhZ2dyZWdhdGlvbiB1c2luZyBkcGx5ci4KCmBgYHtyfQojIGJhciBwbG90IHdpdGggY29sb3VyIGJ5IHZhcmlhYmxlCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24sIGZpbGwgPSBsZWdlbmRhcnkpKSArCiAgZ2VvbV9iYXIoKQoKIyBkcGx5ciBhZ2dyZWdhdGUgZXF1aXZhbGVudApwb2tlbW9uICU+JQogIGNvdW50KGdlbmVyYXRpb24sIGxlZ2VuZGFyeSkKYGBgCgpOb3RpY2UgaW4gdGhlIGFib3ZlIGV4YW1wbGUgdGhhdCB0aGUgYmFycyBieSBkZWZhdWx0IHdlcmUgc3RhY2tlZCBvbiB0b3Agb2YgZWFjaCBvdGhlci4gV2UgaGF2ZSB0d28gb3RoZXIgb3B0aW9ucyBmb3IgY2hhbmdpbmcgdGhpcyB3aXRoIGVpdGhlciBhIGRvZGdlIHNldHRpbmcgKHNpdCBuZXh0IHRvIGVhY2ggb3RoZXIpIG9yIGEgZmlsbCBzZXR0aW5nIChzdGFja2VkIGFuZCBzdGFuZGFyaXNlZCkuIFRvIGNoYW5nZSB0aGlzIHdlIHVzZSB0aGUgcG9zaXRpb24gYXJndW1lbnQgd2l0aGluIGBnZW9tX2JhcigpYC4KCmBgYHtyfQojIGRvZGdlIGJhcnMKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gZ2VuZXJhdGlvbiwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpCiMgZmlsbGVkIGJhcnMKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gZ2VuZXJhdGlvbiwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikKYGBgCgpBIHVzZWZ1bCB0aGluZyB0byBjaGFuZ2Ugd2l0aCBiYXIgcGxvdHMgaXMgdG8gKmZsaXAqIHlvdXIgY29vcmRpbmF0ZXMuIFRoaXMgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCBpZiB5b3VyIHggYXhpcyBjb250YWlucyB0ZXh0LiBJbiB0aGUgZXhhbXBsZSBiZWxvdyB3ZSB3aWxsIHVzZSB0aGUgdHlwZTEgdmFyaWFibGUgYXMgb3VyIHggYXhpcyB0byBzZWUgdGhlIGRpZmZlcmVuY2UuIFdoZW4gd2UgZG9uJ3QgZmxpcCB0aGUgY29vcmRpbmF0ZXMsIHRoZSB4IGF4aXMgaXMgaGFyZCB0byByZWFkLgoKYGBge3J9CmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHR5cGUxLCBmaWxsID0gbGVnZW5kYXJ5KSkgKwogIGdlb21fYmFyKCkKCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHR5cGUxLCBmaWxsID0gbGVnZW5kYXJ5KSkgKwogIGdlb21fYmFyKCkgKyAKICBjb29yZF9mbGlwKCkKYGBgCgpUbyBjaGFuZ2Ugb3VyIGNvbG91cnMgd2UgdXNlIHRoZSBgc2NhbGVfZmlsbF9gIGZ1bmN0aW9uLiBUaGlzIGlzIHZlcnkgc2ltaWxhciB0byB3aGF0IHdlIGRpZCB3aXRoIHNjYXR0ZXIgcGxvdHMgZXhjZXB0IHdlIGFyZSB1c2luZyBmaWxsIHRoaXMgdGltZSwgcmF0aGVyIHRoYW4gY29sb3VyLgoKYGBge3J9CiMgY2hhbmdlIGZpbGwgd2l0aCBSQ29sb3JCcmV3ZXIKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gdHlwZTEsIGZpbGwgPSBsZWdlbmRhcnkpKSArCiAgZ2VvbV9iYXIoKSArIAogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKCiMgY2hhbmdlIGZpbGwgd2l0aCBtYW51YWwgcGFsZXR0ZQpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0eXBlMSwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2JhcigpICsgCiAgY29vcmRfZmxpcCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBtYW51YWxfcGFsKQpgYGAKCkN1cnJlbnRseSBvdXIgcGxvdHMgaGF2ZSB0aGUgZGVmYXVsdCBnZ3Bsb3QgdGhlbWUgd2hpY2ggaGFzIGEgZ3JleSBiYWNrZ3JvdW5kLiBXZSBjYW4gY2hhbmdlIHRoaXMgYnkgc2V0dGluZyBhIG5ldyB0aGVtZS4gVG8gZG8gc28geW91IHVzZSBgdGhlbWVfYCBhbmQgc2VsZWN0IGEgdGhlbWUgd2hpY2ggd29ya3MgYmVzdC4KCmBgYHtyfQojIGNoYW5nZSB0aGVtZSB0byBibGFjayBhbmQgd2hpdGUKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gdHlwZTEsIGZpbGwgPSBsZWdlbmRhcnkpKSArCiAgZ2VvbV9iYXIoKSArIAogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbWFudWFsX3BhbCkgKwogIHRoZW1lX2J3KCkKCiMgY2hhbmdlIHRoZW1lIHRvIGRhcmsKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gdHlwZTEsIGZpbGwgPSBsZWdlbmRhcnkpKSArCiAgZ2VvbV9iYXIoKSArIAogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbWFudWFsX3BhbCkgKwogIHRoZW1lX2RhcmsoKQpgYGAKCkFkZGluZyBhIHRoZW1lIHRvIGVhY2ggcGxvdCBjYW4gYmUgdGlyaW5nLCBzbyBpbnN0ZWFkIHlvdSBjYW4gc2V0IGEgdGhlbWUgZm9yIGFsbCB5b3VyIHBsb3RzIGJ5IHVzaW5nIHRoZSBgdGhlbWVfc2V0KClgIGZ1bmN0aW9uLiBVc3VhbGx5IHlvdSBzZXQgdGhlIHRoZW1lIGJlZm9yZSB5b3UgbWFrZSBhbnkgb2YgeW91ciB2aXN1YWxpc2F0aW9ucy4gTm93IHdlIGhhdmUgY2hhbmdlZCB0aGUgdGhlbWUgdG8gYmxhY2sgYW5kIHdoaXRlLCBhbGwgb3VyIHBsb3RzIGZyb20gbm93IG9uIHdpbGwgaGF2ZSBhIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZS4KCmBgYHtyfQojIHNldCBnbG9iYWwgdGhlbWUKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgojIHNlZSByZXN1bHQKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gdHlwZTEsIGZpbGwgPSBsZWdlbmRhcnkpKSArCiAgZ2VvbV9iYXIoKSArIAogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbWFudWFsX3BhbCkKYGBgCgpJdCBpcyBvZnRlbiB1c2VmdWwgYW5kIGhlbHBmdWwgdG8gYXJyYW5nZSB0aGUgdmFsdWVzIGJ5IHRoZWlyIHJhbmsgb3Igc2l6ZS4gVGhlcmUgYXJlIG9wdGlvbnMgdG8gZG8gdGhpcyB3aXRoIGJhc2UgUiwgYnV0IHRoZSBgZm9yY2F0c2AgbGlicmFyeSBmcm9tIHRoZSB0aWR5dmVyc2UgbWFrZXMgYXJyYW5naW5nIGFuZCBvcmRlcmluZyBmdW5jdGlvbnMgdmVyeSBzdHJhaWdodGZvcndhcmQuCgpXZSB3aWxsIHVzZSB0aGUgYGZjdF9pbmZyZXEoKWAgZnVuY3Rpb24sIHdoaWNoIG1lYW5zIGZhY3RvcnMgaW4gZnJlcXVlbmN5LCBpbiBlZmZlY3Qgb3JkZXJpbmcgb3VyIGZhY3RvcnMgYnkgdGhlIGZyZXF1ZW5jeSB0aGV5IGFwcGVhci4gVGhlcmUgYXJlIHR3byBhcHByb2FjaGVzLiBGaXJzdCB3ZSB1c2UgdGhlIGBmY3RfaW5mcmVxKClgIGZ1bmN0aW9uIHdpdGhpbiBnZ3Bsb3QsIG9yIHNlY29uZCB3ZSBhcnJhbmdlIG91ciBmYWN0b3Igb3V0c2lkZSBnZ3Bsb3QuIE91dHNpZGUgb2YgZ2dwbG90IGlzIHVzdWFsbHkgYmV0dGVyIGFzIHlvdSBoYXZlIG1vcmUgY29udHJvbCBhbmQgaXQgbWFrZSB5b3VyIGdncGxvdCBjb2RlIGVhc2llciB0byByZWFkLgoKYGBge3J9CiMgbG9hZCBmb3JjYXRzCmxpYnJhcnkoZm9yY2F0cykKCiMgYXJyYW5nZSBieSBmcmVxdWVuY3kgd2l0aGluIGdncGxvdApnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSBmY3RfaW5mcmVxKHR5cGUxKSwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2JhcigpICsgCiAgY29vcmRfZmxpcCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBtYW51YWxfcGFsKQoKIyBhcnJhbmdlIGJ5IGZyZXF1ZW5jeSBvdXRzaWRlIGdncGxvdApwb2tlbW9uJHR5cGUxIDwtIGZjdF9pbmZyZXEocG9rZW1vbiR0eXBlMSkKCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHR5cGUxLCBmaWxsID0gbGVnZW5kYXJ5KSkgKwogIGdlb21fYmFyKCkgKyAKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG1hbnVhbF9wYWwpCmBgYAoKV2UgY2FuIGFsc28gcmV2ZXJzZSB0aGUgb3JkZXJpbmcgYnkgcHV0dGluZyBwdXR0aW5nIG91ciBgZmN0X2luZnJlcSgpYCBmdW5jdGlvbiBpbnNpZGUgYSBgZmN0X3JldigpYCBmdW5jdGlvbiAoc3RhbmRzIGZvciBmYWN0b3IgcmV2ZXJzZSkuCgpgYGB7cn0KIyBhcnJhbmdlIGJ5IGZyZXF1ZW5jeSAoZGVzY2VuZGluZykKcG9rZW1vbiR0eXBlMSA8LSBmY3RfcmV2KGZjdF9pbmZyZXEocG9rZW1vbiR0eXBlMSkpCgpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0eXBlMSwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2JhcigpICsgCiAgY29vcmRfZmxpcCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBtYW51YWxfcGFsKQpgYGAKCk1vcmUgaW5mb3JtYXRpb24gb24gdGhlIGZvcmNhdHMgcGFja2FnZSBjYW4gYmUgZm91bmQgaGVyZTogPGh0dHBzOi8vZm9yY2F0cy50aWR5dmVyc2Uub3JnL2luZGV4Lmh0bWw+CgpGaW5hbGx5LCBsZXQncyBzYXZlIGFuZCBsYWJlbCBvdXIgZXhhbXBsZSBiYXIgcGxvdC4KCmBgYHtyfQojIHNhdmUgYW5kIGxhYmVsCmNvdW50X3R5cGUxIDwtIGdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHR5cGUxLCBmaWxsID0gbGVnZW5kYXJ5KSkgKwogIGdlb21fYmFyKCkgKyAKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG1hbnVhbF9wYWwpICsKICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBlYWNoIFBva2Vtb24gdHlwZSIsCiAgICAgICBzdWJ0aXRsZSA9ICJDb2xvdXJlZCBieSBpZiBsZWdlbmRhcnkgb3Igbm90IiwKICAgICAgIHkgPSAiRnJlcXVlbmN5IG9mIFBva2Vtb24gdHlwZSIsCiAgICAgICB4ID0gIlR5cGUgb2YgUG9rZW1vbiIsCiAgICAgICBmaWxsID0gIkxlZ2VuZGFyeSBwb2tlbW9uPyIpCgpjb3VudF90eXBlMQoKZ2dzYXZlKCJjb3VudF90eXBlMS5QTkciLCBjb3VudF90eXBlMSkKYGBgCgojIyBCYXIgcGxvdHMgd2l0aCBjb3VudHMgZXhlcmNpc2UKClVzaW5nIHRoZSBleGFtcGxlcyBhYm92ZSwgbWFrZSBhIHZpc3VhbGlzYXRpb24gb2YgdGhlIGZyZXF1ZW5jeSBvZiBza2kganVtcCBtZWRhbCB3aW5uZXJzIHBlciBjb3VudHJ5ICh0ZWFtKSBmcm9tIHRoZSBPbHltcGljcyBkYXRhc2V0LgoKVHJ5IHRvIGluY2x1ZGU6CgotICAgU2V0dGluZyBhIG5ldyB0aGVtZSB1c2luZyBgdGhlbWVfc2V0KClgLgotICAgT3JkZXIgdGhlIHggYXhpcyBieSB0aGUgZnJlcXVlbmN5IGluIHJldmVyc2Ugb3JkZXIuICpoaW50OiByZW1lbWJlciB0aGUgZm9yY2F0cyBwYWNrYWdlKgotICAgTWFrZSBtZWRhbHMgYSBmYWN0b3IsIHJlLW9yZGVyIHRoZW0sIGFuZCB0aGVuIGNvbG91ciB0aGVtIGxpa2Ugd2UgZGlkIGluIHRoZSBsYXN0IGV4ZXJjaXNlLgotICAgRGVjaWRlIGlmIHBvc2l0aW9uIHN0YWNrLCBkb2RnZSBvciBmaWxsIHdvcmsgYmVzdCB3aXRoIHRoaXMgdmlzdWFsaXNhdGlvbi4KLSAgIEFkZCBhIHRpdGxlIGFuZCBsYWJlbHMuCi0gICBTYXZlIHlvdXIgdmlzdWFsaXNhdGlvbi4KCmBgYHtyfQojIHlvdXIgY29kZSBoZXJlCgoKYGBgCgojIEJhciBwbG90cyB3aXRoIG90aGVyIHN0YXRpc3RpY3MKCkEgdmVyeSB1c2VmdWwgZnVuY3Rpb24gb2YgYmFyIHBsb3RzIGlzIHRvIHNob3cgYSBncm91cCBhdmVyYWdlIGluc3RlYWQgb2YgZnJlcXVlbmN5LiBUaGVyZSBhcmUgdHdvIGFwcHJvYWNoZXMgdG8gc2hvd2luZyBhIGdyb3VwIGF2ZXJhZ2UgaW4gYSBiYXIgcGxvdC4KClRoZSBmaXJzdCByb3V0ZSBpcyBhZ2dyZWdhdGUgeW91ciBkYXRhc2V0LCB0aGVuIGFkZCBpdCBpbnRvIHlvdXIgYmFyIHBsb3QgYXMgc2hvd24gaW4gdGhlIGV4YW1wbGUgYmVsb3cuIFdlIGZpcnN0IHVzZSBgZ3JvdXBfYnkoKWAgYW5kIGBzdW1tYXJpc2UoKWAgZnJvbSBkcGx5ciB0byBmaW5kIGFuIGF2ZXJhZ2UsIGluIHRoaXMgY2FzZSB0aGUgYXZlcmFnZSB0b3RhbCBzdGF0aXN0aWNzIGJ5IHBva2Vtb24gZ2VuZXJhdGlvbi4KCldlIHRoZW4gcHV0IHRoaXMgZGF0YSBpbnRvIGdncGxvdC4gVGhlIGRpZmZlcmVuY2UgZnJvbSBhIG5vcm1hbCBiYXIgcGxvdCBpcyB3ZSBwcm92aWRlIGEgeSBheGlzIChvdXIgY2FsY3VsYXRlZCBhdmVyYWdlKSwgYW5kIGFkZCBgc3RhdCA9ICJpZGVudGl0eSJgIHRvIHRoZSBgZ2VvbV9iYXIoKWAgZnVuY3Rpb24uCgpUaGlzIGlzIGEgZ3JlYXQgYXBwcm9hY2ggYXMgaXQgaXMgZWFzeSB0byBzZWUgd2hhdCBpcyBoYXBwZW5pbmcgYXQgZWFjaCBzdGVwLCBtYWtpbmcgaXQgc2ltcGxlIHRvIGlkZW50aWZ5IGlzc3VlcyBhbmQgbWFrZSBjaGFuZ2VzIGlmIG5lZWRlZC4KCmBgYHtyfQojIGdyb3VwIGFuZCBzdW1tYXJpc2UgdG8gbWFrZSBhdmVyYWdlCmF2Z190b3RhbF9nZW4gPC0gcG9rZW1vbiAlPiUKICBncm91cF9ieShnZW5lcmF0aW9uKSAlPiUKICBzdW1tYXJpc2UoYXZnX3RvdGFsID0gbWVhbih0b3RhbCwgbmEucm0gPSBUUlVFKSkKCiMgcHJpbnQgcmVzdWx0CmF2Z190b3RhbF9nZW4KCiMgYWRkIHRvIGJhciBwbG90IHdpdGggc3RhdCBpZGVudGl0eQpnZ3Bsb3QoYXZnX3RvdGFsX2dlbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gYXZnX3RvdGFsKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKQpgYGAKClRoZSBvdGhlciBhcHByb2FjaCBpcyB0byB1c2UgdGhlIGBzdGF0X3N1bW1hcnkoKWAgZnVuY3Rpb24gdG8gcGVyZm9ybSB0aGUgc2FtZSBwbG90LiBUaGUgZGlmZmVyZW5jZSBmcm9tIGEgbm9ybWFsIGJhciBwbG90IGlzIHdlIGFnYWluIHByb3ZpZGUgdGhlIHkgYXhpcyBidXQgcHJvdmlkZSB0aGUgdmFyaWFibGUgd2Ugd2FudCB0byBhZ2dyZWdhdGUsIHRvdGFsIGluIHRoaXMgY2FzZS4gV2UgdGhlbiBjYWxsIGBzdGF0X3N1bW1hcnkoKWAgYW5kIGFkZCB0d28gYXJndW1lbnRzLCB0aGUgZnVuY3Rpb24gd2Ugd2FudCB0byB1c2UgYW5kIHdoYXQgdHlwZSBvZiBnZW9tZXRyeSB0byB1c2U7IHdlJ3ZlIHVzZWQgbWVhbiBhbmQgYmFyLgoKV2hpbGUgdGhpcyBpcyBsZXNzIGNvZGUsIHdoaWNoIGlzIGEgZ29vZCB0aGluZywgaXQgaXMgaGFyZCB0byB1bmRlcnN0YW5kIHRoZSBzdGVwcyB0YWtlbiB0byBtYWtlIHRoZSBzdW1tYXJ5LgoKYGBge3J9CmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24sIHkgPSB0b3RhbCkpICsKICBzdGF0X3N1bW1hcnkoZnVuID0gIm1lYW4iLCBnZW9tID0gImJhciIpCmBgYAoKV2UgY2FuIGFsc28gYWRkIGVycm9yIGJhcnMgdG8gb3VyIHBsb3RzIHRvIGhlbHAgdXMgdW5kZXJzdGFuZCBob3cgcHJlY2lzZSBvdXIgYXZlcmFnZSBtZWFzdXJlIGlzLiBUbyBhZGQgZXJyb3IgYmFycyBpdCBpcyBnZW5lcmFsbHkgZWFzaWVyIHRvIHVzZSB0aGUgZ3JvdXBfYnkgYW5kIHN1bW1hcmlzZSBhcHByb2FjaC4gV2Ugd2lsbCBsb29rIGF0IHR3byB0eXBlcyBvZiBlcnJvciBiYXJzLCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIGFuZCB0aGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1lYW4uCgpUaGUgc3RhbmRhcmQgZGV2aWF0aW9uIGluZGljYXRlcyBob3cgY2xvc2Ugc2FtcGxlIHZhbHVlcyBhcmUgdG8gdGhlIGF2ZXJhZ2Ugb2YgYWxsIGRhdGEgcG9pbnRzLCBhbmQgdGhlIGFjY3VyYWN5IG9mIHRoZSBhdmVyYWdlLiBUaGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1lYW4gaXMgdGhlIGRpc2NyZXBhbmN5IG9mIHRoZSBzYW1wbGUgbWVhbiBhbmQgdGhlIHRydWUgbWVhbiwgdGVsbGluZyB5b3UgdGhlIGFjY3VyYWN5IG9mIHRoZSBzYW1wbGUgbWVhbi4KClRvIGNhbGN1bGF0ZSwgd2UgZG8gdGhlIHNhbWUgYWdncmVnYXRpb24gYXMgd2UgZGlkIGJlZm9yZSBidXQgYWRkIHNkIChzdGFuZGFyZCBkZXZpYXRpb24pIHRvIHRoZSBzdW1tYXJpc2UgZnVuY3Rpb24gYW5kIGNhbGN1bGF0ZSB0aGUgc2VtIChzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWVhbikgaW4gYSBtdXRhdGUgZnVuY3Rpb24uCgpgYGB7cn0KIyBncm91cCBhbmQgc3VtbWFyaXNlIHRvIG1ha2UgYXZlcmFnZSBhbmQgc2QgcGVyIGdyb3VwCmF2Z190b3RhbF9nZW4gPC0gcG9rZW1vbiAlPiUKICBncm91cF9ieShnZW5lcmF0aW9uKSAlPiUKICBzdW1tYXJpc2UoYXZnX3RvdGFsID0gbWVhbih0b3RhbCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgc2QgPSBzZCh0b3RhbCwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKHNlbSA9IHNkL3NxcnQobGVuZ3RoKHNkKSkpCgojIHByaW50IHJlc3VsdAphdmdfdG90YWxfZ2VuCmBgYAoKVG8gYWRkIGVycm9yIGJhcnMgd2UgdXNlIHRoZSBgZ2VvbV9lcnJvcmJhcigpYCBmdW5jdGlvbiwgd2hpY2ggcmVxdWlyZXMgdHdvIGFyZ3VtZW50cyB3aXRoaW4gYW4gYGFlcygpYCBmdW5jdGlvbiwgdGhlIGB5bWluYCBhbmQgYHltYXhgLiBUbyBmaW5kIGB5bWluYCBvciBgeW1heGAgd2UgcGx1cyBvciBtaW51cyBvdXIgYXZnX3RvdGFsICh5IGF4aXMgdmFsdWUpIGJ5IHRoZSBzZC9zZW0uCgpgYGB7cn0KIyBhZGRpbmcgc3RhbmRhcmQgZGV2aWF0aW9uIGVycm9yIGJhcnMKZ2dwbG90KGF2Z190b3RhbF9nZW4sIGFlcyh4ID0gZ2VuZXJhdGlvbiwgeSA9IGF2Z190b3RhbCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBhdmdfdG90YWwtc2QsIHltYXggPSBhdmdfdG90YWwrc2QpKSArCiAgbGFicyh0aXRsZSA9ICJBdmVyYWdlIFBva2Vtb24gdG90YWwgc3RhdGlzdGljcyBieSBnZW5lcmF0aW9uIiwKICAgICAgIHN1YnRpdGxlID0gIkVycm9yIGJhcnMgaW5kaWNhdGUgc3RhbmRhcmQgZGV2aWF0aW9uIikKCiMgYWRkaW5nIHN0YW5kYXJkIGVycm9yIGJhcnMKZ2dwbG90KGF2Z190b3RhbF9nZW4sIGFlcyh4ID0gZ2VuZXJhdGlvbiwgeSA9IGF2Z190b3RhbCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBhdmdfdG90YWwtc2VtLCB5bWF4ID0gYXZnX3RvdGFsK3NlbSkpICsKICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgUG9rZW1vbiB0b3RhbCBzdGF0aXN0aWNzIGJ5IGdlbmVyYXRpb24iLAogICAgICAgc3VidGl0bGUgPSAiRXJyb3IgYmFycyBpbmRpY2F0ZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWVhbiIpCmBgYAoKWW91IGNhbiBlZGl0IHRoZSBsb29rIG9mIHRoZSBlcnJvciBiYXJzLCBzdWNoIGFzIG1ha2luZyB0aGVtIG5hcnJvd2VyIGFuZCBjaGFuZ2luZyB0aGUgY29sb3VyLiBTZWUgdGhlIGV4YW1wbGUgYmVsb3cgb24gaG93IHRvIGRvIHRoaXMuIFdlJ3ZlIGFsc28gY2hhbmdlZCB0aGUgY29sb3VyIG9mIHRoZSBiYXJzIHRvby4KCmBgYHtyfQpnZ3Bsb3QoYXZnX3RvdGFsX2dlbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gYXZnX3RvdGFsKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIm9yYW5nZSIpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gYXZnX3RvdGFsLXNlbSwgeW1heCA9IGF2Z190b3RhbCtzZW0pLCB3aWR0aCA9IDAuMywgY29sb3VyID0gImRhcmtibHVlIikgKwogIGxhYnModGl0bGUgPSAiQXZlcmFnZSBQb2tlbW9uIHRvdGFsIHN0YXRpc3RpY3MgYnkgZ2VuZXJhdGlvbiIsCiAgICAgICBzdWJ0aXRsZSA9ICJFcnJvciBiYXJzIGluZGljYXRlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtZWFuIikKYGBgCgpJZiB5b3Ugd2FudCB0byBhZGQgZXJyb3IgYmFycyB0byBiYXIgcGxvdHMgd2l0aCBkaWZmZXJlbnQgZ3JvdXBpbmdzIG9uIHRoZSB4IGF4aXMgd2UgbmVlZCB0byBtYWRlIGEgZmV3IHN1YnRsZSBjaGFuZ2VzLCB0aGUgbWFpbiBjaGFuZ2UgaXMgd2UgbmVlZCB0byBoYXZlIGEgZG9kZ2UgYmFyIGNoYXJ0LgoKRmlyc3Qgd2Ugd2lsbCByZSBydW4gb3VyIGF2Z190b3RhbF9nZW4gYWdncmVnYXRpb24gYW5kIGFkZCBhbm90aGVyIGNvbHVtbiB0byBvdXIgZ3JvdXBfYnkuIFdlIHRoZW4gcHJlLWRlZmluZSBob3cgd2lkZSB0aGUgYmFycyBhbmQgZXJyb3IgYmFycyBzaG91bGQgYmUuIEluc3RlYWQgb2YgdXNpbmcgYHBvc2l0aW9uID0gImRvZGdlImAgd2UgdXNlIG91ciBkb2RnZSB2YXJpYWJsZSB3ZSBqdXN0IG1hZGUsIGFuZCBhZGQgdGhlIGZpbGwgdG8gYmUgbGVnZW5kYXJ5IChvdXIgc2Vjb25kIGdyb3VwaW5nKS4KCmBgYHtyfQojIGdyb3VwIGJ5IGxlZ2VuZGFyeSBhcyB3ZWxsCmF2Z190b3RhbF9nZW4gPC0gcG9rZW1vbiAlPiUKICBncm91cF9ieShnZW5lcmF0aW9uLCBsZWdlbmRhcnkpICU+JQogIHN1bW1hcmlzZShhdmdfdG90YWwgPSBtZWFuKHRvdGFsLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBzZCA9IHNkKHRvdGFsLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoc2VtID0gc2Qvc3FydChsZW5ndGgoc2QpKSkKCiMgcHJlLWRlZmluZSB0aGUgZG9kZ2UgcG9zaXRpb24KZG9kZ2UgPC0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjgpCgpnZ3Bsb3QoYXZnX3RvdGFsX2dlbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gYXZnX3RvdGFsLCBmaWxsID0gbGVnZW5kYXJ5KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9IGRvZGdlKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IGF2Z190b3RhbC1zZW0sIHltYXggPSBhdmdfdG90YWwrc2VtKSwgcG9zaXRpb24gPSBkb2RnZSwgd2lkdGggPSAwLjMpICsKICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgUG9rZW1vbiB0b3RhbCBzdGF0aXN0aWNzIGJ5IGdlbmVyYXRpb24iLAogICAgICAgc3VidGl0bGUgPSAiRXJyb3IgYmFycyBpbmRpY2F0ZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWVhbiIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBtYW51YWxfcGFsKQpgYGAKCiMjIEJhciBwbG90cyB3aXRoIG90aGVyIHN0YXRpc3RpY3MgZXhlcmNpc2UKClVzaW5nIHRoZSBleGFtcGxlcyBhYm92ZSBhbmQgdGhlIE9seW1waWNzIGRhdGFzZXQsIG1ha2UgYSB2aXN1YWxpc2F0aW9uIG9mIHRoZSBhdmVyYWdlIGFnZSAobWVhbiBvciBtZWRpYW4pIG9mIEdCUiAoR3JlYXQgQnJpdGFpbikgbWVkYWwgd2lubmVycyBieSBtZWRhbCB0eXBlIGFuZCBnZW5kZXIsIG1ha2luZyBzdXJlIHRvCgotICAgc2hvdyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIG1hbGUgYW5kIGZlbWFsZSBhdGhsZXRlcyB1c2luZyBjb2xvdXJzCi0gICBzaG93IGVycm9yIGJhcnMgZm9yIGVpdGhlciBzdGFuZGFyZCBkZXZpYXRpb24gb3Igc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1lYW4KLSAgIGNvbG91ciwgbGFiZWwgYW5kIHNhdmUgeW91ciB2aXN1YWxpc2F0aW9uCgoqaGludDogZG9uJ3QgZm9yZ290IHRvIHVzZSBkb2RnZSBgPC0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjgpYCoKCmBgYHtyfQojIHlvdXIgY29kZSBoZXJlCgpgYGAKCiMgQmV5b25kIGJhciBwbG90cwoKQmFyIHBsb3RzIGFyZSBub3QgdGhlIG9ubHkgb3B0aW9uIHRvIHZpZXcgYWdncmVnYXRlZCBkYXRhLCBhbmQgdGhlcmUgYXJlIHNvbWUgc291cmNlcyB0aGF0IHN1Z2dlc3QgYmFyIHBsb3RzIGFyZSBsZXNzIHRoYW4gaWRlYWwgZm9yIGFueSB2aXN1YWxpc2F0aW9uIG90aGVyIHRoYW4gc2hvd2luZyB0aGUgZnJlcXVlbmN5IG9mIGEgY29udGludW91cyB2YXJpYWJsZS4gU2VlIDxodHRwczovL3BhdWx2YW5kZXJsYWtlbi5jb20vMjAxOC8xMi8xNy9hdm9pZC1iYXItcGxvdHMtZm9yLWNvbnRpbnVvdXMtZGF0YS1kby10aGlzLWluc3RlYWQvPiBmb3IgZGV0YWlscyBvbiB0aGlzLgoKRm9ydHVuYXRlbHksIHRoZXJlIGFyZSBhbHRlcm5hdGl2ZXMsIHN1Y2ggYXMgYm94IHBsb3RzIHdoaWNoIHdpbGwgYmUgY292ZXJlZCBpbiB0aGUgc2Vjb25kIGRhdGEgdmlzdWFsaXNhdGlvbiB3b3Jrc2hvcCwgb3Igd2UgY2FuIHVzZSBzY2F0dGVyIHBsb3RzISBTY2F0dGVyIHBsb3RzIGFsbG93IHVzIHRvIHNlZSBhbGwgdGhlIGRhdGEgYW5kIHdlIGNhbiBhZGQgb24gYW4gYXZlcmFnZSwgdGhlIGJlc3Qgb2YgYm90aCB3b3JsZHMuCgpJbiBvcmRlciB0byByZWNyZWF0ZSB3aGF0IHdlIGp1c3QgZGlkIHdpdGggYmFyIHBsb3RzIHdpdGggc2NhdHRlciBwbG90cyB3ZSBjYW4gZWl0aGVyIHVzZSBib3RoIGBnZW9tX3BvaW50KClgIGFuZCBgc3RhdF9zdW1tYXJ5KClgLCBvciBtYWtlIGEgc3VtbWFyeSB0YWJsZSBhbmQgYWRkIHRoYXQgdXNpbmcgYSBzZWNvbmQgYGdlb21fcG9pbnQoKWAgZnVuY3Rpb24uIEZpcnN0LCBsZXRzIGp1c3QgcGxvdCB0aGUgZGF0YSBhcyBhIHNjYXR0ZXIgcGxvdCwgbWFraW5nIHRoZSBwb2ludHMgbGFyZ2VyIGFuZCBtb3JlIHRyYW5zcGFyZW50LiBMb3dlcmluZyB0aGUgdHJhbnNwYXJlbmN5IChhbHBoYSkgaXMgaW1wb3J0YW50IGluIHRoZXNlIHBsb3RzIGFzIGRhcmtlciBjb2xvdXJzIGluZGljYXRlIGEgaGlnaGVyIGRlbnNpdHkgb2YgZGF0YSBwb2ludHMuCgpgYGB7cn0KZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gZ2VuZXJhdGlvbiwgeSA9IHRvdGFsKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDUsIGFscGhhID0gLjMzKQpgYGAKCk5vdyB3ZSBjYW4gYWRkIHRoZSBgc3RhdF9zdW1tYXJ5KClgIGZ1bmN0aW9uLiBXZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBtZWFuLCB0aGUgZ2VvbSBpcyBwb2ludCwgYW5kIHRoZSBzaGFwZSBpcyBhIHRoZSBgLWAgc3ltYm9sIChudW1iZXIgOTUpOyB3ZSB3aWxsIGFsc28gbWFrZSB0aGUgc2hhcGUgbGFyZ2VyIHNvIHdlIGNhbiBzZWUgaXQgZWFzaWVyLgoKYGBge3J9CiMgdXNpbmcgc3RhdF9zdW1tYXJ5CmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24sIHkgPSB0b3RhbCkpICsKICBnZW9tX3BvaW50KHNpemUgPSA1LCBhbHBoYSA9IC4zMykgKwogIHN0YXRfc3VtbWFyeShmdW4gPSBtZWFuLCBnZW9tID0gInBvaW50IiwKICAgICAgICAgICAgICAgc2hhcGUgPSA5NSwgc2l6ZSA9IDIwKQpgYGAKCklmIHdlIHVzZSB0aGUgc3VtbWFyeSB0YWJsZSBvcHRpb24gd2UgZmlyc3QgbWFrZSBhIHN1bW1hcnkgdGFibGUgd2l0aCBgZ3JvdXBfYnkoKWAgYW5kIGBzdW1tYXJpc2UoKWAuIFRoZW4gd2UgYWRkIHR3byBgZ2VvbV9wb2ludCgpYCBmdW5jdGlvbnMuIFRoZSBmaXJzdCBoYXMgdGhlIHBva2Vtb24gZGF0YSBhbmQgb3VyIHggYW5kIHkgYXhpcy4gVGhlIHNlY29uZCBpcyBvdXIgc3VtbWFyeSB0YWJsZSwgd2l0aCB0aGUgc2FtZSB4IGF4aXMgYW5kIHRoZSBgYXZnX3RvdGFsYCBhcyB0aGUgeSBheGlzLiAKYGBge3J9CiMgc3VtbWFyeSB0YWJsZSBvcHRpb24KZ2VuX2F2Z190b3RhbCA8LSBwb2tlbW9uICU+JQogIGdyb3VwX2J5KGdlbmVyYXRpb24pICU+JQogIHN1bW1hcmlzZShhdmdfdG90YWwgPSBtZWFuKHRvdGFsLCBuYS5ybSA9IFRSVUUpKQoKZ2VuX2F2Z190b3RhbAoKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHBva2Vtb24sCiAgICAgICAgICAgICBhZXMoeCA9IGdlbmVyYXRpb24sIHkgPSB0b3RhbCksCiAgICAgICAgICAgICBzaXplID0gNSwgYWxwaGEgPSAuMzMpICsKICBnZW9tX3BvaW50KGRhdGEgPSBnZW5fYXZnX3RvdGFsLAogICAgICAgICAgICAgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gYXZnX3RvdGFsKSwKICAgICAgICAgICAgIHNoYXBlID0gOTUsIHNpemUgPSAyMCkKCmBgYAoKRWl0aGVyIG9wdGlvbiB3b3JrcyB3ZWxsLCBidXQgZm9yIHRoZSByZXN0IG9mIHRoZSBleGFtcGxlcyB3ZSB3aWxsIHVzZSB0aGUgYHN0YXRfc3VtbWFyeSgpYCBvcHRpb24gYXMgaXQgaXMgbGVzcyBjb2RlLiAKCk5vdyB3ZSBoYXZlIGFsbCBvdXIgZGF0YSBzbyB3ZSBjYW4gc2VlIHRoZSBudW1iZXIgb2YgcG9pbnRzIGZvciBlYWNoIGdyb3VwLCBhbmQgd2UgY2FuIHNlZSB0aGUgYXZlcmFnZSBwZXIgZ3JvdXAhCgpGaW5hbGx5LCB3ZSBjYW4gYWRkIGNvbG91ciBieSBvdXIgZ3JvdXBlZCB2YXJpYWJsZSAobGVnZW5kYXJ5KSBhbmQgY2hhbmdlIHRoZSBjb2xvdXIgcGFsZXR0ZS4gSnVzdCBsaWtlIHdpdGggdGhlIGJhciBwbG90cyB3ZSBjYW4gYWRqdXN0IHRoZSBwb3NpdGlvbmluZyBmcm9tIHN0YWNrIHRvIGRvZGdlLiBUaGUgZXhhbXBsZXMgYmVsb3cgc2hvdyBib3RoIHN0YWNrIGFuZCBkb2RnZSB2ZXJzaW9ucy4KYGBge3J9CiMgcG9zaXRpb24gc3RhY2tlZApnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwsIGNvbG91ciA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX3BvaW50KHNpemUgPSA1LCBhbHBoYSA9IDAuMykgKyAKICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsCiAgICAgICAgICAgICAgIHNoYXBlID0gOTUsIHNpemUgPSAyMCkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKCiMgcG9zaXRpb24gZG9kZ2UKZG9kZ2UgPC0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjgpCgpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwsIGNvbG91ciA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX3BvaW50KHNpemUgPSA1LCBhbHBoYSA9IDAuMywgcG9zaXRpb24gPSBkb2RnZSkgKyAKICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsCiAgICAgICAgICAgICAgIHNoYXBlID0gOTUsIHNpemUgPSAyMCwKICAgICAgICAgICAgICAgcG9zaXRpb24gPSBkb2RnZSkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKYGBgCgojIyBCZXlvbmQgYmFyIHBsb3RzIGV4ZXJjaXNlCgpSZWNyZWF0ZSB5b3VyIGxhc3QgdmlzdWFsaXNhdGlvbiwgYXZlcmFnZSBhZ2UgKG1lYW4gb3IgbWVkaWFuKSBvZiBHQlIgKEdyZWF0IEJyaXRhaW4pIG1lZGFsIHdpbm5lcnMgYnkgbWVkYWwgdHlwZSBhbmQgZ2VuZGVyLCB1c2luZyB0aGUgYGdlb21fcG9pbnQoKWAgYW5kIGBzdGF0X3N1bW1hcnkoKWAgbWV0aG9kIGRldGFpbGVkIGFib3ZlLgoKYGBge3J9CiMgeW91ciBjb2RlIGhlcmUKCmBgYAoKIyBJbmRpdmlkdWFsIGNvZGluZyBjaGFsbGVuZ2UKCkZvciB0aGUgaW5kaXZpZHVhbCBjb2RpbmcgY2hhbGxlbmdlIHdlIHdpbGwgYmUgdXNpbmcgdGhlIGZvb2QgY29uc3VtcHRpb24gZGF0YSBmcm9tIHRpZHkgVHVlc2RheTogPGh0dHBzOi8vZ2l0aHViLmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvYmxvYi9tYXN0ZXIvZGF0YS8yMDIwLzIwMjAtMDItMTgvcmVhZG1lLm1kPi4KClVzZSB3aGF0IHdlIGhhdmUgY292ZXJlZCBpbiB0aGlzIHdvcmtzaG9wIHRvIG1ha2UgdHdvIHZpc3VhbGlzYXRpb25zIG9mIHRoaXMgZGF0YXNldDoKCi0gICBBIHNjYXR0ZXIgcGxvdCBzaG93aW5nIGNvbnN1bXB0aW9uIGFuZCBjbzIgZW1pc3Npb25zIGZvciBhIHNlbGVjdGVkIGNvdW50cnkgKGUuZy4gVUsgb3IgRnJhbmNlKQotICAgQSBiYXIgcGxvdCBvZiBhdmVyYWdlIGNvMiBlbWlzc2lvbnMgcGVyIGZvb2QgY2F0ZWdvcnkuIERpc3BsYXkganVzdCBzaXggY291bnRyaWVzIHRvIGNvbXBhcmUsIHN1Y2ggYXMgVUssIEZyYW5jZSwgR2VybWFueSBldGMuIGFuZCBjb2xvdXIgdGhlbS4KClVzZSBzb21lIG9mIHRoZSB0aXBzIHdlIHVzZWQgYW5kIHNob3dlZCB0byBtYWtlIHRoZSB2aXN1YWxpc2F0aW9ucyBoYXZlIGxhYmVscywgY29sb3VycyBhbmQgbG9vayBhcHBlYWxpbmcuIFRyeSBhbmQgaGF2ZSBzb21lIGZ1biB3aXRoIGl0ISA9KQoKYGBge3J9CmZvb2RfY29uc3VtcHRpb24gPC0gcmVhZHI6OnJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMjAvMjAyMC0wMi0xOC9mb29kX2NvbnN1bXB0aW9uLmNzdicpCgpmb29kX2NvbnN1bXB0aW9uICU+JQogIGdsaW1wc2UoKQoKIyB5b3VyIGNvZGUgaGVyZQoKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVW5kZXJzdGFuZGluZyB3aGljaCB2aXN1YWxpc2F0aW9uIHRvIHVzZSBhbmQgd2hlbgoKU29tZXRpbWVzIGl0IGNhbiBiZSBoYXJkIHRvIGtub3cgd2hlcmUgdG8gc3RhcnQgd2l0aCBhIHZpc3VhbGlzYXRpb24uIEEgZ3JlYXQgZmlyc3Qgc3RhcnRpbmcgcG9pbnQgaXMgdW5kZXJzdGFuZGluZyB0aGUgb3B0aW9ucyBkZXBlbmRpbmcgb24gdGhlIGRhdGEgdHlwZXMgeW91IGhhdmUgYXZhaWxhYmxlLiBUaGlzIHdlYnNpdGUgZ2l2ZXMgbG90cyBvZiBpbmZvcm1hdGlvbiBhbmQgdmlzdWFsIGd1aWRlcyBvbiB0aGlzIHByb2Nlc3M6IDxodHRwczovL3d3dy5kYXRhLXRvLXZpei5jb20vPgoKIyBTZWVpbmcgd2hhdCBvdGhlcnMgaGF2ZSBkb25lIHdpdGggdGhpcyBkYXRhCgpUaGUgT2x5bXBpY3MgZGF0YSB3ZSB1c2VkIGZvciB0aGUgZXhlcmNpc2VzIHRvZGF5IGlzIGZyb20gdGhlIFRpZHkgVHVlc2RheSBHaXRIdWIgcmVwb3NpdG9yeS4gVGlkeSBUdWVzZGF5IGlzIGEgc29jaWFsIGRhdGEgdmlzdWFsaXNhdGlvbiBjaGFsbGVuZ2UgdGhhdCBoYXBwZW5zIGV2ZXJ5IHdlZWsgYW5kIGlzIGEgZ3JlYXQgd2F5IG9mIGxlYXJuaW5nIGFib3V0IGRhdGEgdml6LgoKVGhlIHRoZSBsaW5rIGJlbG93IHRvIHNlZSB3aGF0IG90aGVycyBoYXZlIGRvbmUgYW5kIHBvc3RlZCBhYm91dCB1c2luZyB0aGUgT2x5bXBpY3MgZGF0YS4gVXNlIGl0IHRvIGdldCBzb21lIGlkZWFzIG9uIHdoYXQgZWxzZSB5b3UgY2FuIHRyeSBhbmQgZG8gb3IgZ2V0IHNvbWUgaW5zcGlyYXRpb24gZnJvbSBvdGhlcnMuIDxodHRwczovL3R3aXR0ZXIuY29tL3NlYXJjaD9sYW5nPWVuJnE9JTIzdGlkeXR1ZXNkYXklMjBvbHltcGljcyZzcmM9dHlwZWRfcXVlcnk+CgojIEZ1biBleHRyYQoKQXMgYSBmdW4gZXh0cmEgeW91IGNhbiBtYW51YWxseSBkZXRlcm1pbmUgc2hhcGVzIGluIHlvdXIgdmlzdWFsaXNhdGlvbiB1c2luZyBgc2NhbGVfc2hhcGVfbWFudWFsKClgLiBXZSd2ZSBhbHNvIHJlbW92ZWQgdGhlIGd1aWRlIHdoaWNoIHdhcyB1bm5lY2Vzc2FyeSBieSB1c2luZyBgZ3VpZGUgPSAibm9uZSJgLiAKCkluIHRoZSBleGFtcGxlIGJlbG93LCBhcyBvdXIgeCBheGlzIGlzIGdlbmVyYXRpb24gZnJvbSAxIHRvIDgsIHdlIGNhbiBtYWtlIGdlbmVyYXRpb24gMSBoYXZlIGEgc2hhcGUgb2YgdGhlIG51bWJlciAxIGFuZCBzbyBvbi4gCmBgYHtyfQpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwsIHNoYXBlID0gZ2VuZXJhdGlvbikpICsKICBnZW9tX3BvaW50KHNpemUgPSA1LCBhbHBoYSA9IC4zMykgKwogIHN0YXRfc3VtbWFyeShmdW4gPSBtZWFuLCBnZW9tID0gInBvaW50IiwKICAgICAgICAgICAgICAgc2hhcGUgPSA5NSwgc2l6ZSA9IDIwKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoNDk6NTYpLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICJub25lIikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIFIgRGF0YSBWaXN1YWxpc3Rpb24gMiAtIE9iamVjdGl2ZSBvZiB3b3Jrc2hvcAoKVG8gY3JlYXRlIGhpc3RvZ3JhbXMsIGJveCwgYW5kIHRpbWUgc2VyaWVzIHBsb3RzIHVzaW5nIHRoZSBnZ3Bsb3QyIHBhY2thZ2UuIAoKIyBXaGF0IHRoaXMgd29ya3Nob3Agd2lsbCBjb3ZlcgoKSW4gdGhpcyB3b3Jrc2hvcCwgdGhlIGFpbSBpcyB0byBjb3ZlciBob3cgdG8gd29yayB3aXRoIGRhdGVzIGluIHBsb3RzLCBhbmQgdXNlIGhpc3RvZ3JhbXMgYW5kIGJveCBwbG90cy4gV2Ugd2lsbCBiZSBjb3ZlcmluZzogCgotICAgSG93IHRvIG1ha2UgYm94IHBsb3RzIHdpdGggZ2dwbG90MgotICAgRGlzcGxheWluZyBkaXN0cmlidXRpb25zIHdpdGggaGlzdG9ncmFtcwotICAgV29ya2luZyB3aXRoIGRhdGVzIHdpdGggdGhlIGx1YnJpZGF0ZSBwYWNrYWdlCi0gICBIb3cgdG8gbWFrZSB0aW1lIHNlcmllcyBsaW5lIHBsb3RzCi0gICBIb3cgdG8gc3BsaXQgeW91ciBkYXRhIGludG8gZmFjZXQgZ3JpZHMKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKSW4gdGhpcyBkYXRhIHZpc3VhbGlzYXRpb24gd29ya3Nob3Agd2Ugd2lsbCBiZSBidWlsZGluZyBvbiB0aGUgY29uY2VwdHMgbGVhcm50IGluIHRoZSBmaXJzdCB3b3Jrc2hvcCwgY29uc3RydWN0aW5nIHZpc3VhbGlzYXRpb25zIHVzaW5nIHRoZSBgZ2dwbG90MmAgbGlicmFyeS4gCgohW10oaHR0cHM6Ly9naXRodWIuY29tL2FuZHJld21vbGVzMi9yVHJhaW5JbnRyb2R1Y3Rpb24vYmxvYi9tYWluL3ItZGF0YS12aXN1YWxpc2F0aW9uLTEvaW1hZ2VzL2dncGxvdDJfbWFzdGVycGllY2UucG5nP3Jhdz10cnVlKXt3aWR0aD0iNTQxIn0KCldlIHdpbGwgYmUgdXNpbmcgb25lIG5ldyBwYWNrYWdlIGNhbGxlZCAqbHVicmlkYXRlKiwgYSB0aWR5dmVyc2UgcGFja2FnZSB3aGljaCBpcyBkZXNpZ25lZCB0byBtYWtlIHdvcmtpbmcgd2l0aCBkYXRlcyBhbmQgdGltZXMgZWFzaWVyOyB0aGlzIHdpbGwgaGVscCB1cyBpbiBtYWtpbmcgdGltZSBzZXJpZXMgdmlzdWFsaXNhdGlvbnMuICoqUnVuIHRoZSB0aGUgY29kZSBiZWxvdyB0byBpbnN0YWxsIGx1YnJpZGF0ZSoqLiAKCmBgYHtyIGV2YWw9RkFMU0V9CiMgaW5zdGFsbCBsdWJyaWRhdGUKaW5zdGFsbC5wYWNrYWdlcygibHVicmlkYXRlIikKYGBgCgpCZWZvcmUgd2Ugc3RhcnQgd2Ugd2lsbCBuZWVkIHRvIGxvYWQgdGhlIGxpYnJhcmllcyB3ZSB3aWxsIGJlIHVzaW5nIGR1cmluZyB0aGlzIHNlc3Npb24uICoqUnVuIHRoZSBjb2RlIGJlbG93IHRvIGxvYWQgeW91ciBsaWJyYXJpZXMqKi4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGxpYnJhcmllcyB3ZSB3aWxsIGJlIHVzaW5nCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKIyBCb3ggcGxvdHMKCkJveCBwbG90cyBhcmUgZGVzaWduZWQgdG8gY29tcGFyZSB0aGUgZGlmZmVyZW5jZXMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSAoc2FtcGxlcyBvciBncm91cHMpLiBUaGV5IGRvIHRoaXMgYnkgZGlzcGxheWluZyB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIGEgY29udGludW91cyB2YXJpYWJsZSAoZS5nLiBudW1lcmljKSBmb3IgZWFjaCBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gCgpUaGUgc3VtbWFyeSBzdGF0aXN0aWNzIHNob3duIGFyZTogCgotIFRoZSBtZWRpYW4gKG1pZGRsZSB2YWx1ZSkKLSBJbnRlcnF1YXJ0aWxlIHJhbmdlLCBrbm93biBhcyBJUVIsIHdoaWNoIGhhcyB2YWx1ZXMgZnJvbSAyNSUgdG8gNzUlIChvciAyNXRoIHRvIDc1dGggcGVyY2VudGlsZSkKLSBGaXJzdCBxdWFydGlsZSwga25vd24gYXMgUTEsIHdoaWNoIGhhcyBhIHZhbHVlIG9mIDI1JQotIFNlY29uZCBxdWFydGlsZSwga25vd24gYXMgUTMsIHdoaWNoIGhhcyBhIHZhbHVlIG9mIDc1JQotICJtaW5pbXVtIiB2YWx1ZSwgY2FsY3VsYXRlZCBhcyBgUTEgLSAxLjUqSVFSYAotICJtYXhpbXVtIiB2YWx1ZSwgY2FsY3VsYXRlZCBhcyBgUTMgKyAxLjUqSVFSYAotIE91dGxpZXIsIHdoaWNoIGFyZSB2YWx1ZXMgdGhhdCBmYWxsIG91dHNpZGUgb2YgdGhlIG1heGltdW0gb3IgbWluaW11bSB2YWx1ZXMKCldlIHdpbGwgdXNlIGRhdGEgZnJvbSB0aGUgUG9rw6ltb24gZ2FtZXMgYWdhaW4gZm9yIG91ciBleGFtcGxlcyBmb3IgYm94IHBsb3RzLCB3aGljaCB3YXMgd2ViIHNjcmFwZWQgZnJvbSA8aHR0cHM6Ly9wb2tlbW9uZGIubmV0L3Bva2VkZXgvYWxsPi4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgbG9hZCBhbmQgY2xlYW4gbmFtZXMKcG9rZW1vbiA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FuZHJld21vbGVzMi93ZWJTY3JhcGluZy9tYWluL1IvZGF0YS9wb2tlbW9uLmNzdiIpICU+JQogIGNsZWFuX25hbWVzKCkKIyByZXZpZXcgZGF0YQpwb2tlbW9uICU+JQogIGdsaW1wc2UoKQpgYGAKCkZvciB0aGVzZSBleGFtcGxlcyB3ZSB3aWxsIGp1c3QgbG9vayBhdCBvbmUgdHlwZSBvZiBQb2vDqW1vbiwgdGhlIGVsZWN0cmljIHR5cGU7IHRoZSBtb3N0IGZhbW91cyBvZiB3aGljaCBpcyBQaWthY2h1ISBGaXJzdCwgd2UgZXh0cmFjdCBqdXN0IHRoZSBlbGVjdHJpYyB0eXBlIFBva8OpbW9uLCBhbmQgbWFrZSByZWxldmFudCBjb2x1bW5zIGZhY3RvcnMuIAoKYGBge3J9CiMgc2VsZWN0IGNvbHVtbnMgdG8gY29udmVydCB0byBmYWN0b3IKdG9fZmFjdG9yIDwtIGMoInR5cGUxIiwgInR5cGUyIiwgImdlbmVyYXRpb24iKQoKIyBleHRyYWN0IGp1c3QgZWxlY3RyaWMgcG9rZW1vbiBhbmQgbWFrZSBjb2xzIGZhY3RvcnMKZWxlY3RyaWNfcG9rZW1vbiA8LSBwb2tlbW9uICU+JQogIGZpbHRlcih0eXBlMSA9PSAiRWxlY3RyaWMiIHwgdHlwZTIgPT0gIkVsZWN0cmljIikgJT4lCiAgbXV0YXRlKGFjcm9zcyhhbGxfb2YodG9fZmFjdG9yKSwgZmFjdG9yKSkKCmhlYWQoZWxlY3RyaWNfcG9rZW1vbikKYGBgCgpUbyBtYWtlIGEgYm94IHBsb3QgaW4gZ2dwbG90IHdlIHVzZSB0aGUgYGdlb21fYm94cGxvdCgpYCBnZW9tIGZ1bmN0aW9uLiBPbmUgb2Ygb3VyIGF4aXMgdmFyaWFibGVzIGhhcyB0byBiZSBjYXRlZ29yaWNhbCBhbmQgdGhlIG90aGVyIGhhcyB0byBiZSBudW1lcmljLiBJbiB0aGUgYmVsb3cgZXhhbXBsZSB3ZSB3aWxsIHVzZSBnZW5lcmF0aW9uIChjYXRlZ29yaWNhbCkgYW5kIHRvdGFsIChudW1lcmljKS4gCgpgYGB7cn0KIyBnZW5lcmF0aW9uIGJ5IHRvdGFsCmdncGxvdChlbGVjdHJpY19wb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24sIHkgPSB0b3RhbCkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCkZyb20gdGhlIG91dHB1dCB3ZSBzZWUgYSBmZXcgdGhpbmdzLiBGaXJzdCBpcyB0aGF0IGVhY2ggYm94IGhhcyBhIGxpbmUgdGhyb3VnaCB0aGUgbWlkZGxlIHdoaWNoIGluZGljYXRlcyB0aGUgbWVkaWFuOyB0aGUgYm94IGl0c2VsZiBpcyBvdXIgaW50ZXJxdWFydGlsZSByYW5nZS4gVGhlIGxpbmVzIGFib3ZlIGFuZCBiZWxvdyB0aGUgYm94ZXMgKGtub3duIGFzIHdoaXNrZXJzKSBhcmUgdGhlIG1heGltdW0gYW5kIG1pbmltdW0gdmFsdWVzLiBUaGUgYmxhY2sgZG90cyBpbmRpY2F0ZSBvdXRsaWVycywgd2hpY2ggaGF2ZSBmYWxsZW4gb3V0c2lkZSBvdXIgbWF4IGFuZCBtaW4gdmFsdWVzLiAKCkp1c3QgbGlrZSB3aXRoIHNjYXR0ZXIgYW5kIGJhciBwbG90cyB3ZSBjYW4gY2hhbmdlIHRoZSBjb2xvdXJzISBZb3UgY2FuIHVzZSBlaXRoZXIgZmlsbCBvciBjb2xvdXIgYXJndW1lbnRzIHdpdGggYm94IHBsb3RzLCBidXQgZmlsbCB0ZW5kcyB0byBsb29rIGJldHRlci4gCgpXZSB3aWxsIHVzZSB0aGUgY29sb3VyIG9mIFBpa2FjaHUgdG8gY29sb3VyIG91ciBib3hlcy4gV2UgdXNlZCB0aGUgcG9rZW1vbiBjb2xvdXIgcGlja2VyIHRvIGdldCB0aGUgY29sb3VyIG9mIHBpa2FjaHU6IDxodHRwczovL3Bva2VwYWxldHRlcy5jb20vI3Bpa2FjaHU+CgpgYGB7cn0KZ2dwbG90KGVsZWN0cmljX3Bva2Vtb24sIGFlcyh4ID0gZ2VuZXJhdGlvbiwgeSA9IHRvdGFsKSkgKwogIGdlb21fYm94cGxvdChmaWxsID0gIiNmNmU2NTIiKQpgYGAKClNvbWV0aW1lcyBpdCBpcyB1c2VmdWwgdG8gcmVtb3ZlIHRoZSBvdXRsaWVycy4gVG8gZG8gc28geW91IGFkZCBpbiB0aGUgYG91dGxpZXIuc2hhcGUgPSBOQWAgYXJndW1lbnQuIAoKYGBge3J9CmdncGxvdChlbGVjdHJpY19wb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24sIHkgPSB0b3RhbCkpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICIjZjZlNjUyIiwgb3V0bGllci5zaGFwZSA9IE5BKQpgYGAKCkRpc3BsYXlpbmcgb3V0bGllcnMgaXMgdXN1YWxseSBhIGdvb2QgaWRlYSBzbyB3ZSB3aWxsIGtlZXAgdGhlbSBmb3Igbm93LCBhbmQgY2hhbmdlIHRoZSBjb2xvdXIgYW5kIHNoYXBlIG9mIHRoZW0uIFRvIGFkanVzdCB0aGVzZSB3ZSB1c2UgYG91dGxpZXIuY29sb3VyYCBhbmQgYG91dGxpZXIuc2hhcGVgIGFyZ21lbnRzLiBXZSd2ZSB1c2VkIHRoZSBjb2xvdXIgb2YgUGlrYWNodSdzIGNoZWVrcyBhcyB0aGUgb3V0bGllciBjb2xvdXIgYW5kIG1hZGUgdGhlIHNoYXBlIHNxdWFyZS4gCmBgYHtyfQpnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuY29sb3VyID0gIiNjNTIwMTgiLAogICAgICAgICAgICAgICBvdXRsaWVyLnNoYXBlID0gMTUpCmBgYAoKIyMgQm94IHBsb3RzIGV4ZXJjaXNlIAoKRm9yIHRoZSBleGVyY2lzZXMgZm9yIHRoaXMgd29ya3Nob3BzIHdlIHdpbGwgYmUgdXNpbmcgZGFpbHkgQ09WSUQgZGF0YSB0aGF0IGlzIGNvbGxlY3RlZCBmcm9tIG1vc3Qgb2YgdGhlIGNvdW50cmllcyBhcm91bmQgdGhlIHdvcmxkLiAKCkNPVklEIGRhdGEgaXMgZnJvbSBvdXIgd29ybGQgaW4gZGF0YSwgd2hpY2ggaXMgc3RvcmVkIGluIGEgR2l0SHViIHJlcG9zaXRvcnkuIE1vcmUgaW5mb3JtYXRpb24gb24gdGhlIGRhdGEgYW5kIHdoYXQgZWFjaCB2YXJpYWJsZSBtZWFucyBjYW4gYmUgZm91bmQgaGVyZTogPGh0dHBzOi8vZ2l0aHViLmNvbS9vd2lkL2NvdmlkLTE5LWRhdGEvdHJlZS9tYXN0ZXIvcHVibGljL2RhdGE+CgpgYGB7cn0KIyBsb2FkIGluIGNvdmlkIGRhdGEgYW5kIHNlbGVjdCBjYXNlcywgZGVhdGhzIGFuZCB2YWNjaW5lcwpjb3ZpZCA8LSByZWFkX2NzdigiaHR0cHM6Ly9jb3ZpZC5vdXJ3b3JsZGluZGF0YS5vcmcvZGF0YS9vd2lkLWNvdmlkLWRhdGEuY3N2IikgJT4lCiAgc2VsZWN0KGlzb19jb2RlOm5ld19kZWF0aHNfc21vb3RoZWRfcGVyX21pbGxpb24sIGNvbnRhaW5zKCJ2YWNjaW4iKSwKICAgICAgICAgcG9wdWxhdGlvbiwgbWVkaWFuX2FnZSwgZ2RwX3Blcl9jYXBpdGEpCgojIGhhdmUgYSBxdWljayBsb29rIGF0IHRoZSBkYXRhCmNvdmlkICU+JSBnbGltcHNlKCkKYGBgCgpGb3IgdGhpcyBleGVyY2lzZSB3aWxsIHdlIG1ha2UgdHdvIGJveCBwbG90cyBmcm9tIG91ciBkYXRhIGxvb2tpbmcgbW9yZSBhdCB0aGUgZGVtb2dyYXBoaWNzIG9mIGVhY2ggY29udGluZW50ICh3ZSB3aWxsIGxvb2sgYXQgY2FzZXMgYW5kIHZhY2NpbmVzIGxhdGVyKS4KCllvdXIgdHdvIGJveCBwbG90cyBzaG91bGQgc2hvdyB0aGUgZm9sbG93aW5nOgoKLSBUaGUgbWVkaWFuIGFnZSBvZiBlYWNoIGNvbnRpbmVudAotIFRoZSBnZHAgcGVyIGNhcGl0YSBmb3IgZWFjaCBjb250aW5lbnQKLSBNYWtlIHN1cmUgdG8gY2hhbmdlIHRoZSBjb2xvdXIgb2YgdGhlIGJveGVzIGFuZCBvdXRsaWVycyB0byBtYWtlIGl0IGxvb2sgYmV0dGVyISAKLSBUcnkgY2hhbmdpbmcgdGhlIHNoYXBlIGFuZCBzaXplIG9mIHlvdXIgb3V0bGllcgoKSGludDogeW91IHdpbGwgaGF2ZSB0byByZW1vdmUgdGhlIG5hIHZhbHVlcyBmcm9tIGNvbnRpbmVudCBiZWZvcmUgcGxvdHRpbmcsIGUuZy4gYGNvdmlkICU+JSBmaWx0ZXIoIWlzLm5hKGNvbnRpbmVudCkpYAoKSGludDogWW91IGNhbiBwaXBlIGZyb20geW91ciBmaWx0ZXIgZnVuY3Rpb24gc3RyYWlnaHQgaW50byBnZ3Bsb3QyIQoKSGludDogWW91IGNhbiBhZGQgY29sb3VycyBpbiBsb3RzIG9mIHdheXMgYnV0IGl0IGNhbiBiZSBmdW4gdG8gdXNlIGEgY29sb3VyIHBpY2tlciA8aHR0cDovL3RyaXN0ZW4uY2EvaGNsLXBpY2tlci8jL2hsYy8xMS8xLjEvREM3MjYxL0Q3NzM1Nz4uIAoKYGBge3J9CiMgeW91ciBjb2RlIGhlcmUKCgpgYGAKCgojIEltcHJvdmluZyB5b3VyIGJveCBwbG90cwoKVGhlIG1haW4gaXNzdWUgd2l0aCBib3ggcGxvdHMsIGluIGEgc2ltaWxhciB3YXkgdG8gYmFyIHBsb3RzLCBpcyB0aGV5IGNhbiBoaWRlIGRhdGEuIFdlIGNhbiBmaXggdGhpcyBieSBhZGRpbmcgYSBzY2F0dGVyIHBsb3Qgb3ZlciB0aGUgdG9wIG9mIHRoZSBib3hlcyBzbyB3ZSBjYW4gc2VlIHRoZSBmdWxsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YS4gCgpXaGVuIGFkZGluZyBpbiBhIHNjYXR0ZXIgcGxvdCwgd2Ugd29uJ3QgbmVlZCBvdXIgb3V0bGllcnMgYXMgdGhlIHNjYXR0ZXIgcGxvdCB3aWxsIHNob3cgdGhlc2UgZm9yIHVzLiBXZSB3aWxsIG5lZWQgdG8gcmVtb3ZlIHRoZW0gdXNpbmcgdGhlIGBvdXRsaWVyLnNoYXBlID0gTkFgIGFyZ3VtZW50LiAKCmBgYHtyfQpnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGdlb21fcG9pbnQoKQpgYGAKClNvbWUgb2Ygb3VyIGRhdGEgcG9pbnRzIGFyZSBvdmVybGFwcGluZyB3aGljaCBtYWtlcyBpdCBhIGxpdHRsZSBoYXJkIHRvIHNlZSBhbGwgdGhlIGRhdGEuIFdlIGNhbiBmaXggdGhpcyBieSBjaGFuZ2luZyB0aGUgcG9zaXRpb24gb2Ygb3VyIHBvaW50cyB1c2luZyB0aGUgYHBvc2l0aW9uID0gImppdHRlciJgIGFyZ3VtZW50LiBXZSBjYW4gYWxzbyB1c2UgYGdlb21faml0dGVyKClgIHdoaWNoIGlzIGEgc2hvcnQgaGFuZCBmb3IgYGdlb21fcG9pbnQocG9zaXRpb24gPSAiaml0dGVyIilgOyB3ZSB3aWxsIHVzZSBgZ2VvbV9qaXR0ZXIoKWAgZ29pbmcgZm9yd2FyZCBhcyBpdCBpcyBsZXNzIHR5cGluZy4gCgpgYGB7cn0KIyBjaGFuZ2UgcG9zaXRpb24gaW4gZ2VvbV9wb2ludApnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGdlb21fcG9pbnQocG9zaXRpb24gPSAiaml0dGVyIikKCiMgdXNpbmcgZ2VvbV9qaXR0ZXIKZ2dwbG90KGVsZWN0cmljX3Bva2Vtb24sIGFlcyh4ID0gZ2VuZXJhdGlvbiwgeSA9IHRvdGFsKSkgKwogIGdlb21fYm94cGxvdChmaWxsID0gIiNmNmU2NTIiLCBvdXRsaWVyLnNoYXBlID0gTkEpICsKICBnZW9tX2ppdHRlcigpCmBgYAoKV2UgY2FuIGFsc28gYWRkIGluIGEgY29sb3VyIGdyb3VwaW5nIHRvIG91ciBwb2ludHMgdG8gbWFrZSB0aGVtIG1vcmUgbWVhbmluZ2Z1bC4gV2UgYWRkIHRoZSBjb2xvdXIgYWVzdGhldGljIHRvIG91ciBgZ2VvbV9qaXR0ZXJgIGZ1bmN0aW9uLiBJbiB0aGUgZXhhbXBsZSB3ZSBhcmUgY29sb3VyaW5nIG91ciBwb2ludHMgYnkgaWYgYSBwb2tlbW9uIGlzIGxlZ2VuZGFyeSBvciBub3QuIAoKYGBge3J9CmdncGxvdChlbGVjdHJpY19wb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24sIHkgPSB0b3RhbCkpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICIjZjZlNjUyIiwgb3V0bGllci5zaGFwZSA9IE5BKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG91ciA9IGxlZ2VuZGFyeSkpCmBgYAoKRmluYWxseSB3ZSBjYW4gY2hhbmdlIHRoZSBjb2xvdXJzIG9mIG91ciBwb2ludHMsIHdoaWNoIGluIHRoaXMgY2FzZSB3ZSBoYXZlIGRvbmUgbWFudWFsbHkuIEFnYWluLCB0aGUgY29sb3VycyB3ZXJlIHRha2VuIGZyb20gdGhlIHBva2Vtb24gY29sb3VyIHBpY2tlciBvZiBwaWthY2h1OiA8aHR0cHM6Ly9wb2tlcGFsZXR0ZXMuY29tLyNwaWthY2h1Pi4KCmBgYHtyfQpnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBsZWdlbmRhcnkpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjYzUyMDE4IiwgIiM0MTQxNGEiKSkKYGBgCgpOb3cgd2UgY2FuIGFkZCBhIHRpdGxlIGFuZCBzYXZlIHRoZSBwbG90ISBXaGVuIHNhdmluZyB0aGUgcGxvdCB3ZSBoYXZlIG1hbnVhbGx5IGFkanVzdGVkIHRoZSB3aWR0aCBvZiB0aGUgcGxvdC4gWW91IGNhbiBhbHNvIGNoYW5nZSB0aGUgaGVpZ2h0LiAKCmBgYHtyfQplbGVjdHJpY19wb2tlbW9uX2JveCA8LSBnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBsZWdlbmRhcnkpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjYzUyMDE4IiwgIiM0MTQxNGEiKSkgKwogIGxhYnModGl0bGUgPSAiU3VtbWFyeSBvZiBlbGVjdHJpYyBwb2tlbW9uIGZvciBlYWNoIGdlbmVyYXRpb24iKSArCiAgdGhlbWVfYncoKQoKZWxlY3RyaWNfcG9rZW1vbl9ib3gKCmdnc2F2ZSgiZWxlY3RyaWNfcG9rZW1vbl9ib3gucG5nIiwgZWxlY3RyaWNfcG9rZW1vbl9ib3gsCiAgICAgICB3aWR0aCA9IDUuNSkKYGBgCgoKIyMgSW1wcm92aW5nIHlvdXIgYm94IHBsb3RzIGV4ZXJjaXNlCgpGb3IgdGhpcyBleGVyY2lzZSB3ZSB3aWxsIGxvb2sgYXQgdmFjY2luZXMhIFdlIHdpbGwgbG9vayBhdCAxMCBjb3VudHJpZXMgdG8gc2VlIHRoZSBkaWZmZXJlbmNlIGluIHZhY2NpbmUgZGlzdHJpYnV0aW9uOyA1IGhhdmUgbG93IGdkcCBhbmQgNSBoYXZlIGhpZ2ggZ2RwLiBUaGUgZGF0YSB3aWxsIGJlIHByZS1wcmVwYXJlZCBmb3IgeW91LiBXZSBoYXZlIG1hZGUgYSB2ZWN0b3Igd2l0aCB0aGUgY291bnRpZXMgdGhhdCBoYXZlIGhpZ2ggYW5kIGxvdyBnZHAuIFRoZW4gd2UgaGF2ZSBmaWx0ZXJlZCBvdXIgY292aWQgZGF0YSBieSB0aGlzIHZlY3RvciwgYW5kIG1hZGUgdGhlIGxvY2F0aW9uIGEgb3JkZXJlZCBmYWN0b3IuCgoxKSBNYWtlIGEgYm94IHBsb3QgdXNpbmcgdGhlICpjb3ZpZF9zZWxlY3RfY291bnRyaWVzKiBkYXRhLCB3aXRoIHggPSBsb2NhdGlvbiBhbmQgeSA9IHRvdGFsX3ZhY2NpbmF0aW9uc19wZXJfaHVuZHJlZC4gQmUgc3VyZSB0byBpbmNsdWRlIGBnZW9tX2ppdHRlcigpYC4KMikgTm93IGltcHJvdmUgdGhlIGxvb2sgb2YgeW91ciBib3ggcGxvdCEgQ2hhbmdlIHRoZSBjb2xvdXIgb2YgdGhlIGJveGVzIGFuZCB0aGUgcG9pbnRzLCBtYWtlIHRoZSBwb2ludHMgbW9yZSB0cmFuc3BhcmVudCwgcmVtb3ZlIHRoZSBvdXRsaWVycywgY2hhbmdlIHRoZSB0aGVtZSwgYW5kIGZsaXAgdGhlIGNvLW9yZGluYXRlcy4gCjMpIE1ha2UgYW5vdGhlciBib3ggcGxvdCB0aGUgc2FtZSB3YXkgYnV0IHVzZSB0aGUgcGVvcGxlX2Z1bGx5X3ZhY2NpbmF0ZWQgdmFyaWFibGUgYXMgeW91ciB5IGF4aXMuIAo0KSBHaXZlIGJvdGggeW91ciBib3ggcGxvdHMgYSB0aXRsZSBhbmQgY2hhbmdlIHRoZSBheGlzIGxhYmVscyAoaWYgeW91IHdhbnQpLgo1KSBTYXZlIHlvdXIgcGxvdHMgdXNpbmcgYGdnc2F2ZSgpYC4gWW91IHdpbGwgbmVlZCB0byBhc3NpZ24gdGhlIHBsb3RzIHRvIGEgdmFyaWFibGUgZmlyc3QuIAoKYGBge3J9CiMgTWFrZSB2ZWN0b3Igd2l0aCBsb3cgYW5kIGhpZ2ggZ2RwIGNvdW50cmllcwpoaWdoX2xvd19nZHAgPC0gYygiU2llcnJhIExlb25lIiwgIkV0aGlvcGlhIiwiWWVtZW4iLCAKICAgICAgICAgICAgICAgICAgIlphbWJpYSIsICJOZXBhbCIsICJTd2VkZW4iLCAiQXVzdHJhbGlhIiwKICAgICAgICAgICAgICAgICAgIlNhdWRpIEFyYWJpYSIsICJHZXJtYW55IiwgIlVuaXRlZCBLaW5nZG9tIikKCiMgT25seSBpbmNsdWRlIGxvY2F0aW9ucyBpbiBoaWdoX2xvd19nZHAKIyBNYWtlIGxvY2F0aW9uIGEgZmFjdG9yLCBvcmRlcmVkIGJ5IGhpZ2hfbG93X2dkcApjb3ZpZF9zZWxlY3RfY291bnRyaWVzIDwtIGNvdmlkICU+JQogIGZpbHRlcihsb2NhdGlvbiAlaW4lIGhpZ2hfbG93X2dkcCkgJT4lCiAgbXV0YXRlKGxvY2F0aW9uID0gZmFjdG9yKGxvY2F0aW9uLCBsZXZlbHMgPSBoaWdoX2xvd19nZHApKQoKIyB5b3VyIGNvZGUgaGVyZQoKCmBgYAoKIyBEaXNwbGF5aW5nIGRpc3RyaWJ1dGlvbnMgd2l0aCBoaXN0b2dyYW1zCgpIaXN0b2dyYW1zIGFyZSBncmVhdCBmb3IgdmlzdWFsaXNpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBudW1lcmljIGRhdGEuIEhpc3RvZ3JhbXMgaGF2ZSBvbmUgbnVtZXJpY2FsIHZhcmlhYmxlIGFzIHRoZWlyIGlucHV0LiAKClRvIG1ha2UgYSBoaXN0b2dyYW0gd2l0aCBnZ3Bsb3Qgd2UgcHJvdmlkZSBhIG51bWVyaWNhbCB2YWx1ZSB0byBvdXIgeCBheGlzLCBhbmQgdXNlIHRoZSBgZ2VvbV9oaXN0b2dyYW0oKWAgZ2VvbS4gSW4gdGhlIGV4YW1wbGUgd2UgYXJlIHVzaW5nIGFsbCB0aGUgcG9rZW1vbiBkYXRhIGFuZCBzaG93aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHRvdGFsIGNvbHVtbi4gCmBgYHtyfQpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCkpICsKICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKV2UgY2FuIGFkanVzdCB0aGUgc2l6ZSBvZiB0aGUgKmJpbnMqIG9mIG91ciBwbG90IHdpdGggdHdvIG1ldGhvZHMsIGNoYW5naW5nIHRoZSBiaW53aWR0aCBvciBzZWxlY3RpbmcgdGhlIGFtb3VudCBvZiBiaW5zLiBXaGVuIHdlIHRhbGsgYWJvdXQgYmlucyB3aXRoIGhpc3RvZ3JhbXMgaXQgcmVmZXJzIHRvIHRoZSBzaXplIG9mIGVhY2ggYmFyOyB0aGUgbGFyZ2VyIHRoZSBiYXIgdGhlIG1vcmUgZGF0YSBvbiB0aGUgeCBheGlzIGlzIGluY2x1ZGVkLiAKClRoZSBmaXJzdCBleGFtcGxlIHVzZXMgYGJpbndpZHRoYC4gVGhlIG51bWJlciB5b3UgcHJvdmlkZSBpcyBkaXJlY3RseSByZWxhdGVkIHRvIHlvdXIgeCBheGlzLiBJbiBvdXIgZXhhbXBsZSB3ZSBhcmUgdXNpbmcgdGhlIHRvdGFsIGNvbHVtbiB3aGljaCBnb2VzIHVwIHRvIDc1NC4gSWYgd2UgaGF2ZSBgYmlud2lkdGggPSA4YCwgdGhlbiA4IGRhdGEgcG9pbnRzIHdpbGwgYmUgaW5jbHVkZWQgaW4gZWFjaCBiaW4uIFJ1biB0aGUgdHdvIGV4YW1wbGVzIGJlbG93IHdpdGggYSBzbWFsbGVyIGFuZCBsYXJnZXIgYmlud2lkdGggdG8gc2VlIHRoZSByZXN1bHRzLiAKYGBge3J9CiMgc3VtbWFyeSBzdGF0cyBmb3IgdG90YWwgY29sdW1uCnN1bW1hcnkocG9rZW1vbiR0b3RhbCkKCiMgYmlud2lkdGggb2YgOApnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDgpICsKICBsYWJzKHRpdGxlID0gIlNtYWxsIGJpbndpZHRoICg4KSIpCgojIGJpbndpZHRoIG9mIDUwCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHRvdGFsKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTApICsKICBsYWJzKHRpdGxlID0gIkxhcmdlciBiaW53aWR0aCAoNTApIikKYGBgCgpUaGUgb3RoZXIgbWV0aG9kIGlzIHRvIHNlbGVjdCB0aGUgbnVtYmVyIG9mIGJpbnMgdG8gdXNlLCB1c2luZyB0aGUgYGJpbnNgIGFyZ3VtZW50LiBUaGUgbW9yZSBiaW5zIHdlIHVzZSwgdGhlIGxlc3MgZGF0YSB3aWxsIGJlIGNvbnRhaW5lZCBpbiBlYWNoIGJpbi4gSW4gdGhlIGV4YW1wbGUgYmVsb3cgd2UgaGF2ZSBiaW5zIHdpdGggbG90cyBvZiBkYXRhIGBiaW5zID0gMTBgIGFuZCBiaW5zIHdpdGggbGVzcyBkYXRhIGBiaW5zID0gNTBgLiBXaGljaCBkbyB5b3UgdGhpbmsgaXMgYmVzdD8KCmBgYHtyfQojIHVzaW5nIDEwIGJpbnMKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gdG90YWwpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwKSArCiAgbGFicyh0aXRsZSA9ICJMZXNzIGJpbnMgPSBtb3JlIGRhdGEgaW4gZWFjaCBiaW4iKQoKIyB1c2luZyA1MCBiaW5zCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHRvdGFsKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MCkgKwogIGxhYnModGl0bGUgPSAiTW9yZSBiaW5zID0gbGVzcyBkYXRhIGluIGVhY2ggYmluIikKYGBgCgpJdCBjYW4gYmUgaGVscGZ1bCB0byBjb2xvdXIgeW91ciBoaXN0b2dyYW0gYnkgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gVGhpcyB3b3JrcyB0aGUgc2FtZSBhcyBhIGJveCBwbG90LCB1c2luZyB0aGUgYGZpbGxgIGFyZ3VtZW50LiBJbiB0aGUgZXhhbXBsZSB3ZSBoYXZlIGZpbGxlZCBvdXIgaGlzdG9ncmFtIGJ5IHRoZSBsZWdlbmRhcnkgY2F0ZWdvcnkuIAoKYGBge3J9CmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHRvdGFsLCBmaWxsID0gbGVnZW5kYXJ5KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMjApCmBgYAoKQW5vdGhlciB1c2VmdWwgbWV0aG9kIGlzIHRvIHVzZSAqZmFjZXRzKiwgd2hpY2ggc3BsaXQgdXAgeW91ciBkYXRhIGJ5IGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgYW5kIHByZXNlbnRzIHRoZW0gaW4gYSBncmlkIGxpa2UgZm9ybWF0aW9uLiAKClRoZXJlIGFyZSB0d28gdGVjaG5pcXVlcyBpbiBnZ3Bsb3QgdG8gbWFrZSBmYWNldHMsIHVzaW5nIGBmYWNldF9ncmlkKClgIG9yIGBmYWNldF93cmFwKClgLiBUbyB1c2UgYGZhY2V0X2dyaWQoKWAgd2UgZGVmaW5lIGlmIHdlIHdhbnQgdG8gZGlzcGxheSBvdXIgZGF0YSByb3ctd2lzZSAoYHJvd3MgPSBgKSBvciBjb2x1bW4td2lzZSAoYGNvbHMgPSBgKS4gV2hlbiBkZWZpbmluZyB3aGljaCBjb2x1bW4gdG8gc3BsaXQgb3VyIGRhdGEgYnkgd2UgbmVlZCB0byB1c2UgdGhlIGB2YXJzKClgIGZ1bmN0aW9uLiBTZWUgdGhlIHR3byBleGFtcGxlcyBiZWxvdyBvbiBob3cgdG8gZG8gYSByb3cgb3IgY29sdW1uIGZhY2V0IGdyaWQuIAoKYGBge3J9CiMgcm93LXdpc2UgZGlzcGxheQpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwKSArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhsZWdlbmRhcnkpKSArCiAgbGFicyh0aXRsZSA9ICJSb3ctd2lzZSBmYWNldCBncmlkIikKCiMgY29sdW1uLXdpc2UgZGlzcGxheQpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwKSArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhsZWdlbmRhcnkpKSArCiAgbGFicyh0aXRsZSA9ICJjb2x1bW4td2lzZSBmYWNldCBncmlkIikKYGBgCgpUaGUgb3RoZXIgb3B0aW9uIGlzIGBmYWNldF93cmFwKClgLCB3aGljaCBieSBkZWZhdWx0IG9ubHkgbmVlZHMgdGhlIGNvbHVtbiB5b3Ugd2FudCB0byBzcGxpdCB5b3VyIGRhdGEgYnkuIEl0IGRvZXMgYWxsb3cgZXh0cmEgc3BlY2lmaWNhdGlvbiB3aXRoIHRoZSBgbnJvd2AgYW5kIGBuY29sYCBmdW5jdGlvbnMsIGFsbG93aW5nIHlvdSB0byBkZWZpbmUgaG93IG1hbnkgcm93cyBhbmQgY29sdW1ucyB0byBkaXNwbGF5LiAKCkluIHRoZSBleGFtcGxlcyBiZWxvdyB3ZSBzaG93IHRoZSBkZWZhdWx0IGBmYWNldF93cmFwYCwgYW5kIGhvdyB0byBhZGp1c3QgdGhlIGNvbHVtbiBvciByb3cgc3BlY2lmaWNhdGlvbi4gV2UgaGF2ZSB1c2VkIHRoZSBnZW5lcmF0aW9uIGNvbHVtbiBhcyBpdCBoYXMgbW9yZSBncm91cHMuICAKYGBge3J9CiMgZGVmYXVsdCBmYWNldF93cmFwCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHRvdGFsLCBmaWxsID0gbGVnZW5kYXJ5KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMjApICsKICBmYWNldF93cmFwKHZhcnMoZ2VuZXJhdGlvbikpICsKICBsYWJzKHRpdGxlID0gIkRlZmF1bHQgZmFjZXQgd3JhcCIpCgojIDQgcm93cwpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwKSArCiAgZmFjZXRfd3JhcCh2YXJzKGdlbmVyYXRpb24pLAogICAgICAgICAgICAgbnJvdyA9IDQpICsKICBsYWJzKHRpdGxlID0gIkZhY2V0IHdyYXAgd2l0aCA0IHJvd3MiKQoKIyA0IGNvbHVtbnMKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gdG90YWwsIGZpbGwgPSBsZWdlbmRhcnkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyMCkgKwogIGZhY2V0X3dyYXAodmFycyhnZW5lcmF0aW9uKSwKICAgICAgICAgICAgIG5jb2wgPSA0KSArCiAgbGFicyh0aXRsZSA9ICJGYWNldCB3cmFwIHdpdGggNCBjb2x1bW5zIikKYGBgCgoKIyMgRGlzcGxheWluZyBkaXN0cmlidXRpb25zIGV4ZXJjaXNlCgpGb3IgdGhpcyBleGVyY2lzZSB3ZSB3aWxsIGJlIG1ha2luZyBhIGhpc3RvZ3JhbSBvZiB1c2luZyB0aGUgcGVvcGxlX2Z1bGx5X3ZhY2NpbmF0ZWRfcGVyX2h1bmRyZWQgY29sdW1uIGZvciBlYWNoIGNvbnRpbmVudCAKCi0gTWFrZSBhIGhpc3RvZ3JhbSB3aXRoIHBlb3BsZV9mdWxseV92YWNjaW5hdGVkX3Blcl9odW5kcmVkIGFzIHlvdXIgeCBheGlzCi0gQWRkIGEgZmlsbCBhcmdtZW50IHdpdGggY29udGluZW50Ci0gQWRqdXN0IHRoZSBgYmlud2lkdGhgIG9yIGBiaW5zYCAoZS5nLiBgYmlud2lkdGggPSA1YCBsb29rcyBnb29kKQotIFVzaW5nIFJDb2xvdXJCcmV3ZXIsIGFkanVzdCB0aGUgY29sb3VycyB1c2VkIGluIGZpbGwKCkhpbnQ6IHlvdSB3aWxsIGhhdmUgdG8gcmVtb3ZlIHRoZSBuYSB2YWx1ZXMgZnJvbSBjb250aW5lbnQgYmVmb3JlIHBsb3R0aW5nLCBlLmcuIGBjb3ZpZCAlPiUgZmlsdGVyKCFpcy5uYShjb250aW5lbnQpKWAKCkhpbnQ6IFlvdSBjYW4gcGlwZSBmcm9tIHlvdXIgZmlsdGVyIGZ1bmN0aW9uIHN0cmFpZ2h0IGludG8gZ2dwbG90MiEKCkhpbnQ6IFRvIGNoYW5nZSB0aGUgZmlsbCBjb2xvdXJzIHlvdSBjYW4gdXNlIGBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gImEgcGFsZXR0ZSIpYAoKSGludDogVXNlIGBicmV3ZXIucGFsLmluZm9gIHRvIGZpbmQgUkNvbG9yQnJld2VyIHBhbGV0dGVzCgpgYGB7cn0KIyB5b3VyIGNvZGUgaGVyZQoKYGBgCgoKIyBXb3JraW5nIHdpdGggdGhlIGRhdGUgZGF0YSB0eXBlIHdpdGggbHVicmlkYXRlCgpXb3JraW5nIHdpdGggdGhlIGRhdGUgZGF0YSB0eXBlIHdoZW4gcHJvZ3JhbW1pbmcgY2FuIGJlIGEgYml0IHRyaWNreSBmb3IgbWFueSByZWFzb25zLiBUaGVyZSBhcmUgZGlmZmVyZW50IGZvcm1hdHMsIHRpbWUgem9uZXMsIGFuZCB0aGUgY2hhbGxlbmdlIGV4dHJhY3RpbmcgaW5mb3JtYXRpb24gZnJvbSB0aGUgZGF0ZS4gRm9ydHVuYXRlbHksIHRoZSBgbHVicmlkYXRlYCBwYWNrYWdlIGNvbWVzIHRvIHRoZSByZXNjdWUhIAoKVGhlcmUgYXJlIHRocmVlIHR5cGVzIG9mIGRhdGUgZGF0YSB0eXBlOiBkYXRlICgyMDEwLTA5LTAxKSwgdGltZSAoMTU6MDg6NTIgQlNUKSwgZGF0ZS10aW1lICgyMDEwLTA5LTAxIDE1OjA4OjUyIEJTVCkuIEZvciB0aGlzIHdvcmtzaG9wIHdlIHdpbGwgYmUgZm9jdXNpbmcgb24gdGhlIGRhdGUgdHlwZSBhcyBpdCBpcyB0aGUgbW9zdCBjb21tb24uIAoKWW91IGNhbiBmaW5kIG91dCB0b2RheSdzIGRhdGUgKG1vcmUgdXNlZnVsIHRoYW4gaXQgc291bmRzKSBvciB0aGUgZGF0ZSBhbmQgdGltZSB1c2luZyB0aGUgYHRvZGF5KClgIG9yIGBub3coKWAgZnVuY3Rpb25zLiAKYGBge3J9CiMgbWFrZSBzdXJlIGRwbHlyIGFuZCBsdWJyaWRhdGUgYXJlIGxvYWRlZApsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMgZ2V0IHRvZGF5J3MgZGF0ZQp0b2RheSgpCiMgdG9kYXkncyBkYXRlIGFuZCB0aW1lCm5vdygpCgojIG1ha2UgdG9kYXkncyBkYXRlIGEgdmFyaWFibGUKdG9kYXlfZGF0ZSA8LSB0b2RheSgpCmBgYAoKQSBncmVhdCBmZWF0dXJlIG9mIGx1YnJpZGF0ZSBpcyBleHRyYWN0aW5nIHRoZSB5ZWFyLCBtb250aCwgZGF5LCBvciB3ZWVrIGRheSBpbmZvcm1hdGlvbiBmcm9tIHlvdXIgZGF0ZS4gV2UgY2FuIHRlc3QgaXQgb3V0IG9uIHRvZGF5J3MgZGF0ZS4gUnVuIHRoZSBjb2RlIHRvIHNlZSBob3cgdGhlIG91dHB1dC4gCgpgYGB7cn0KIyB5ZWFyCnllYXIodG9kYXlfZGF0ZSkKIyBtb250aAptb250aCh0b2RheV9kYXRlKQptb250aCh0b2RheV9kYXRlLCBsYWJlbCA9IFRSVUUpCiMgd2Vlawp3ZWVrKHRvZGF5X2RhdGUpCiMgZGF5CmRheSh0b2RheV9kYXRlKQojIHdlZWtkYXkKd2RheSh0b2RheV9kYXRlKQp3ZGF5KHRvZGF5X2RhdGUsIGxhYmVsID0gVFJVRSkKYGBgCgpOb3RpY2UgdGhhdCBmb3IgdGhlIGBtb250aGAgYW5kIGB3ZGF5YCBmdW5jdGlvbnMgd2UgaGF2ZSB0aGUgb3B0aW9uIHRvIGFkZCBsYWJlbHMuIFRoaXMgY2FuIGJlIHZlcnkgdXNlZnVsLCBtYWtpbmcgeW91ciBtb250aCBvciB3ZWVrIGRheSBvdXRwdXRzIG1vcmUgcmVhZGFibGUuIAoKRm9yIHRoZSByZXN0IG9mIHRoZSBleGFtcGxlcyB3ZSB3aWxsIHVzZSBzb21lIHJhbmRvbWlzZWQgbWFkZSB1cCBkYXRhIGNvbnRhaW5pbmcgZGFpbHkgc2xlZXAsIGFuZCBzdGVwIGluZm9ybWF0aW9uLiBSdW4gdGhlIGNvZGUgYmVsb3cgdG8gc2VlIHRoZSBkYXRhLiAKCipub3RlOiB0byBtYWtlIHRoaXMgZGF0YSB3ZSBoYXZlIHVzZWQgcmFuZG9taXNhdGlvbiBmdW5jdGlvbnM6IGBzYW1wbGVgLCBgcnVuaWZgIGFuZCBgcm5vcm1gLCBpZiB5b3UgYXJlIGludGVyZXN0ZWQgbG9vayB0aGVtIHVwIHRvIHNlZSBob3cgdGhleSB3b3JrKgpgYGB7cn0KIyBtYWtlIHNvbWUgcmFuZG9tIGRhdGEKZGYgPC0gZGF0YS5mcmFtZSgKICBkYXRlID0gc2VxKGFzLkRhdGUoIjIwMTktMDEtMDEiKSwgYXMuRGF0ZSgiMjAyMS0xMi0wMSIpLCBieSA9ICJkYXlzIiksCiAgaG91cnNfc2xlZXAgPSByb3VuZChybm9ybSgxMDY2LCBtZWFuID0gOSwgc2QgPSAxLjUpKSwKICBzdGVwcyA9IHJvdW5kKHJub3JtKDEwNjYsIG1lYW4gPSA4MDAwLCBzZCA9IDIwMDApKQopCgpoZWFkKGRmKQpgYGAKCldlIGNhbiBub3cgdXNlIHRoZSBgbXV0YXRlYCBmdW5jdGlvbiB0byBtYWtlIGEgeWVhciwgbW9udGgsIHdlZWssIGRheSwgYW5kIHdlZWsgZGF5IGNvbHVtbi4gCgpgYGB7cn0KZGYgPC0gZGYgJT4lCiAgbXV0YXRlKHllYXIgPSB5ZWFyKGRhdGUpLAogICAgICAgICBtb250aCA9IG1vbnRoKGRhdGUsIGxhYmVsID0gVFJVRSksCiAgICAgICAgIHdlZWsgPSB3ZWVrKGRhdGUpLAogICAgICAgICBkYXkgPSBkYXkoZGF0ZSksCiAgICAgICAgIHdlZWtfZGF5ID0gd2RheShkYXRlLCBsYWJlbCA9IFRSVUUpKQoKaGVhZChkZikKCiMgc2VlIHRoZSBicmVha2Rvd24gb2YgdGhlIGRhdGUKZGZbMToyLCBjKCJkYXRlIiwgInllYXIiLCAibW9udGgiLCAid2VlayIsICJkYXkiLCAid2Vla19kYXkiKV0KYGBgCgpCcmVha2luZyB0aGUgZGF0ZSBkb3duIGluIHRoaXMgd2F5IGFsbG93cyB1cyB0byBkbyBzb21lIGFnZ3JlZ2F0aW9uIG9mIG91ciBkYXRhIGJ5IHRoZSB5ZWFyLCBtb250aCwgd2VlaywgZGF5LCBvciB3ZWVrZGF5ISBJbiB0aGUgZXhhbXBsZXMgYmVsb3cgd2UgaGF2ZSBzaG93biB5ZWFyIGFuZCB3ZWVrZGF5LgoKYGBge3J9CiMgYWdncmVnYXRlIGJ5IHllYXIKZGYgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXNlKGF2Z19zbGVlcCA9IG1lYW4oaG91cnNfc2xlZXApLAogICAgICAgICAgICBhdmdfc3RlcHMgPSBtZWFuKHN0ZXBzKSwKICAgICAgICAgICAgdG90YWxfc3RlcHMgPSBzdW0oc3RlcHMpKQoKIyBhZ2dyZWdhdGUgYnkgd2VlayBkYXkKZGYgJT4lCiAgZ3JvdXBfYnkod2Vla19kYXkpICU+JQogIHN1bW1hcmlzZShhdmdfc2xlZXAgPSBtZWFuKGhvdXJzX3NsZWVwKSwKICAgICAgICAgICAgYXZnX3N0ZXBzID0gbWVhbihzdGVwcyksCiAgICAgICAgICAgIHRvdGFsX3N0ZXBzID0gc3VtKHN0ZXBzKSkKYGBgIAoKVGhlcmUgYXJlIG1vcmUgZnVuY3Rpb25zIGZyb20gdGhlIGx1YnJpZGF0ZSBwYWNrYWdlIHRoYXQgd2Ugd29uJ3QgYmUgYWJsZSB0byBjb3ZlciBpbiB0aGlzIHNlc3Npb24sIHNvIGRvIGhhdmUgYSBsb29rIGF0IHRoZSBwYWNrYWdlIHdlYnNpdGUgZm9yIG1vcmUgaW5mb3JtYXRpb24gLSA8aHR0cHM6Ly9sdWJyaWRhdGUudGlkeXZlcnNlLm9yZy9pbmRleC5odG1sPiAtIGFuZCBjaGVja291dCB0aGUgUiBmb3IgRGF0YSBTY2llbmNlIGNoYXB0ZXIgb24gZGF0ZXMgLSA8aHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRlcy1hbmQtdGltZXMuaHRtbD4uIAoKIyMgbHVicmlkYXRlIGV4ZXJjaXNlCgpVc2luZyB0aGUgZXhhbXBsZXMgYWJvdmUsIGV4dHJhY3QgeWVhciwgbW9udGgsIGRheSwgZGF5IG9mIHdlZWsgZnJvbSBjb3ZpZCBkYXRhLCBhbmQgZG8gYW4gYWdncmVnYXRpb24hIAoKMSkgQWRkIG5ldyBjb2x1bW5zIHRvIHlvdXIgY292aWQgZGF0YSBmb3IgeWVhciwgbW9udGgsIHdlZWssIGRheSBhbmQgd2Vla19kYXkuIFRyeSB0byBhZGQgbGFiZWxzIHRvIG1vbnRoIGFuZCB3ZWVrX2RheS4gCjIpIEFnZ3JlZ2F0ZSB5b3VyIGNvdmlkIGRhdGEgYnkgeWVhciBhbmQgbW9udGggdG8gZmluZCB0aGUgbWVhbiB0b3RhbCBjYXNlcyBwZXIgbWlsbGlvbiBhbmQgbWVhbiB0b3RhbCBkZWF0aHMgcGVyIG1pbGxpb24uIAozKSBQcmludCBvdXQgdGhlIHJlc3VsdC4gCmBgYHtyfQojIHlvdXIgY29kZSBoZXJlCgojIHNlcGFyYXRlIGRhdGUgY29sdW1uCmNvdmlkIDwtIGNvdmlkICU+JQogIG11dGF0ZSh5ZWFyID0geWVhcihkYXRlKSwKICAgICAgICAgbW9udGggPSBtb250aChkYXRlLCBsYWJlbCA9IFRSVUUpLAogICAgICAgICB3ZWVrID0gd2VlayhkYXRlKSwKICAgICAgICAgZGF5ID0gZGF5KGRhdGUpLAogICAgICAgICB3ZWVrX2RheSA9IHdkYXkoZGF0ZSwgbGFiZWwgPSBUUlVFKSkKCiMgbWFrZSB5ZWFyIGFuZCBtb250aCBhZ2dyZWdhdGUKYXZnX3llYXJfbW9udGhfY292aWQgPC0gY292aWQgJT4lCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgpICU+JQogIHN1bW1hcmlzZSgKICAgIGF2Z190b3RhbF9jYXNlc19wZXJfbWlsID0gbWVhbih0b3RhbF9jYXNlc19wZXJfbWlsbGlvbiwgbmEucm0gPSBUUlVFKSwKICAgIGF2Z190b3RhbF9kZWF0aHNfcGVyX21pbCA9IG1lYW4odG90YWxfZGVhdGhzX3Blcl9taWxsaW9uLCBuYS5ybSA9IFRSVUUpCiAgICApCgphdmdfeWVhcl9tb250aF9jb3ZpZApgYGAKCgojIFRpbWUgc2VyaWVzIHBsb3RzCgpUaW1lIHNlcmllcyBwbG90cyB2aXN1YWxpc2UgZGF0YSBvdmVyIGEgcGVyaW9kIG9mIHRpbWUsIHdoaWNoIGNhbiBiZSBob3VybHksIGRhaWx5LCB3ZWVrbHksIG1vbnRobHksIG9yIHllYXJseS4gSXQgaXMgYSBncmVhdCB3YXkgdG8gdmlldyB0cmVuZHMgb3ZlciB0aW1lLiBXaGVuIHBsb3R0aW5nIGEgdGltZSBzZXJpZXMsIHRoZSB4IGF4aXMgaXMgdGhlIGRhdGUgYW5kIHRoZSB5IGF4aXMgaXMgeW91ciBtZWFzdXJlLiAKClRoZSBtb3N0IHNpbXBsZSBmb3JtIG9mIGEgdGltZSBzZXJpZXMgdmlzdWFsaXNhdGlvbiBpbiBSIGlzIHRvIHVzZSBhbiB1bmVkaXRlZCBkYXRlIHZhcmlhYmxlLiBVc2luZyBvdXIgZXhhbXBsZSBkYXRhIChgZGZgKSB3ZSB3aWxsIHZpc3VhbGlzZSBob3cgc3RlcHMgaGF2ZSBjaGFuZ2VkIGVhY2ggZGF5LiAKYGBge3J9CiMgZGFpbHkgdGltZSBzZXJpZXMKZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IHN0ZXBzKSkgKwogIGdlb21fbGluZSgpCmBgYAoKQXMgd2UgY2FuIHNlZSBpdCBpcyBwcmV0dHkgdmFyaWFibGUgaG93IG1hbnkgc3RlcHMgYXJlIHRha2VuIGVhY2ggZGF5LCBhcyB5b3UgbWlnaHQgZXhwZWN0LiBUaGVyZSBpcyBhIGxvdCBvZiBkYXRhIGhlcmUgc28gaXQgaXMgaGFyZCB0byBzZWUgYW55IHJlYWwgcGF0dGVybnMsIGl0IGp1c3QgbG9va3MgbGlrZSBub2lzZSEgVG8gc29sdmUgdGhpcyB3ZSBjYW4gYWdncmVnYXRlIG91ciBkYXRhIGJ5IHRoZSB5ZWFyLCB0aGUgbW9udGggb3IgdGhlIHdlZWsgdG8gc2VlIGlmIHdlIGNhbiBnZXQgYW55IG1vcmUgaW5zaWdodHMuIAoKRm9yIHRoZSBleGFtcGxlIGRhdGEgd2UgaGF2ZSBpdCBtaWdodCBiZSBpbnRlcmVzdGluZyB0byBzZWUgdGhlIGF2ZXJhZ2Ugb2YgaG93IG1hbnkgc3RlcHMgYXJlIHRha2VuIG9uIGF2ZXJhZ2UgZWFjaCBtb250aCwgYW5kIHRvIGFsc28gY29tcGFyZSB0aGlzIHllYXIgb24geWVhci4KCldlIGZpcnN0IGFnZ3JlZ2F0ZSBvdXIgZGF0YSwgZ3JvdXBpbmcgYnkgdGhlIG1vbnRoIGFuZCB5ZWFyIGNvbHVtbnMgd2UgbWFkZSB3aXRoIHRoZSBsdWJyaWRhdGUgcGFja2FnZSwgZmluZCB0aGUgYXZlcmFnZSBzdGVwcywgYW5kIGNvbnZlcnQgdGhlIHllYXIgY29sdW1uIGludG8gYSBmYWN0b3IgdG8gbWFrZSBwbG90dGluZyBlYXNpZXI7IG1vbnRoIGlzIGFscmVhZHkgYSBmYWN0b3IuIApgYGB7cn0KIyBhZ2dyZWdhdGVkIHRpbWUgc2VyaWVzIGJ5IG1vbnRoCm1vbnRobHlfc3RlcHMgPC0gZGYgJT4lCiAgZ3JvdXBfYnkobW9udGgsIHllYXIpICU+JQogIHN1bW1hcmlzZShhdmdfc3RlcHMgPSBtZWFuKHN0ZXBzKSkgJT4lCiAgbXV0YXRlKHllYXIgPSBmYWN0b3IoeWVhcikpCgptb250aGx5X3N0ZXBzCmBgYAoKTm93IHdlIGNhbiBtYWtlIGEgdGltZSBzZXJpZXMgYnkgbW9udGghIEl0IGlzIG9mdGVuIGhlbHBmdWwgd2hlbiB1c2luZyBgZ2VvbV9saW5lKClgIHRvIGFsc28gcGFpciBpdCB3aXRoIGBnZW9tX3BvaW50KClgIHNvIHdlIGNhbiBzZWUgZWFjaCBkYXRhIHBvaW50IGNsZWFybHkgYXMgd2VsbCBhcyBzZWVpbmcgdGhlIHRyZW5kcyB3aXRoIHNob3duIGJ5IHRoZSBsaW5lcy4gCgpgYGB7cn0KZ2dwbG90KG1vbnRobHlfc3RlcHMsCiAgICAgICBhZXMoeCA9IG1vbnRoLCB5ID0gYXZnX3N0ZXBzKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkKYGBgCgpUaGF0IGRpZG4ndCB3b3JrIGFzIGV4cGVjdGVkISBBcyBvdXIgZGF0YSBpcyBncm91cGVkIGJ5IHllYXIgYW5kIG1vbnRoIHdlIG5lZWQgdG8gdXNlIHRoZSBgZ3JvdXAgPSBgIGFyZ3VtZW50IHRvIHRlbGwgZ2dwbG90IHdlIHdhbnQgdG8gY29ubmVjdCB0aGUgbW9udGhzIHVwLiAKCkJ5IGFkZGluZyBgZ3JvdXAgPSB5ZWFyYCBvdXIgcGxvdCB3aWxsIG5vdyBsb29rIGxpa2UgYSB0aW1lIHNlcmllcywgcnVuIHRoZSBjb2RlIHRvIGNoZWNrIGl0IG91dC4gCgpgYGB7cn0KZ2dwbG90KG1vbnRobHlfc3RlcHMsCiAgICAgICBhZXMoeCA9IG1vbnRoLCB5ID0gYXZnX3N0ZXBzLAogICAgICAgICAgIGdyb3VwID0geWVhcikpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKSXQgd291bGQgYWxzbyBiZSBoZWxwZnVsIHRvIHNlZSB3aGF0IHllYXIgZWFjaCBsaW5lIHJlcHJlc2VudHMuIFdlIGFkZCB0aGUgYGNvbG91ciA9IHllYXJgIGFyZ3VtZW50IGluIGFzIHdlbGwgdG8gc2hvdyB0aGlzLiAKCmBgYHtyfQpnZ3Bsb3QobW9udGhseV9zdGVwcywKICAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBhdmdfc3RlcHMsCiAgICAgICAgICAgZ3JvdXAgPSB5ZWFyLCBjb2xvdXIgPSB5ZWFyKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkKYGBgCgpPdXIgcGxvdCBpcyBzdGlsbCBsb29raW5nIGEgbGl0dGxlIGJ1c3kgc28gd2UgY2FuIHVzZSBmYWNldHMgdG8gc3BsaXQgb3VyIGRhdGEgYnkgeWVhci4gV2UndmUgdXNlZCBgZmFjZXRfd3JhcGAgaGVyZSB3aXRoIDMgcm93cy4gCgpgYGB7cn0KZ2dwbG90KG1vbnRobHlfc3RlcHMsCiAgICAgICBhZXMoeCA9IG1vbnRoLCB5ID0gYXZnX3N0ZXBzLAogICAgICAgICAgIGdyb3VwID0geWVhciwgY29sb3VyID0geWVhcikpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBmYWNldF93cmFwKHZhcnMoeWVhciksIG5yb3cgPSAzKQpgYGAKCkZpbmFsbHksIHdlIGNhbiBtYWtlIGEgZmV3IGZpbmFsIGFkanVzdG1lbnRzIGFuZCB3ZSBoYXZlIGEgbmljZSB2aXN1YWxpc2F0aW9uIHRoYXQgc2hvd3MgYXZlcmFnZSBzdGVwIGNvdW50IHBlciBtb250aCBmb3IgdGhlIHllYXIgMjAxOSB0byAyMDIxLiBCZWxvdyBpcyBhIGxpc3Qgb2YgYWxsIHRoZSBhZGRpdGlvbnMgbWFrZSB0byBjaGFuZ2UgdGhlIGxvb2sgb2YgdGhlIHBsb3Q6CgotIENoYW5nZWQgdGhlIHNpemUgb2YgdGhlIGxpbmVzIGFuZCB0aGUgcG9pbnRzIHdpdGggdGhlIGBzaXplID0gYCBhcmd1bWVudAotIEFkZGVkIGEgdGl0bGUgYW5kIGNoYW5nZWQgdGhlIGF4aXMgbmFtZXMKLSBBZGRlZCBhIGNvbG91ciBzY2FsZSBmcm9tIHRoZSBSQ29sb3JCcmV3ZXIgcGFja2FnZQotIENoYW5nZWQgdGhlIHRoZW1lIHRvIGRhcmsgYW5kIGNoYW5nZWQgdGhlIGZvbnQgdG8gQXZlbmlyCi0gQWRqdXN0ZWQgdGhlIHkgYXhpcyBsaW1pdHMKCmBgYHtyfQpzdGVwX2NvdW50IDwtIGdncGxvdChtb250aGx5X3N0ZXBzLAogICAgICAgYWVzKHggPSBtb250aCwgeSA9IGF2Z19zdGVwcywKICAgICAgICAgICBncm91cCA9IHllYXIsIGNvbG91ciA9IHllYXIpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAyLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgZmFjZXRfd3JhcCh2YXJzKHllYXIpLCBucm93ID0gMykgKwogIGxhYnModGl0bGUgPSAiQXZlcmFnZSBzdGVwIGNvdW50IHBlciBtb250aCBmb3IgdGhlIHllYXIgMjAxOSB0byAyMDIxIiwKICAgICAgIHggPSAiTW9udGgiLCB5ID0gIkF2ZXJhZ2Ugc3RlcHMgKG1lYW4pIiwKICAgICAgIGNvbG91ciA9ICJZZWFyIikgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJQYXN0ZWwyIikgKwogIHRoZW1lX2RhcmsoYmFzZV9mYW1pbHkgPSAiQXZlbmlyIikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDcwMDAsIDkwMDApKSAKCnN0ZXBfY291bnQKCmdnc2F2ZSgic3RlcF9jb3VudC5wbmciLCBzdGVwX2NvdW50LCB3aWR0aCA9IDkpCmBgYAoKIyMgVGltZSBzZXJpZXMgcGxvdHMgZXhlcmNpc2UKCkZvciB0aGlzIGV4ZXJjaXNlIHdlIHdpbGwgYmUgbG9va2luZyBhdCB0aGUgdmFjY2luZSByb2xsIG91dCBmb3IgVW5pdGVkIEtpbmdkb20sIEluZGlhLCBOZXBhbCwgSXNyYWVsLCBHZXJtYW55LCBhbmQgQXVzdHJhbGlhLiBFYWNoIGNvdW50cnkgaGFzIGhhZCBzbGlnaHRseSBkaWZmZXJlbnQgcm9sbCBvdXRzLCB3aXRoIElzcmFlbCBiZWluZyB0aGUgZmFzdGVzdC4gV2Ugd2lsbCBiZSBsb29raW5nIGF0IHRoZSB3ZWVrIGJ5IHdlZWsgcm9sbCBvdXQgZm9yIDIwMjEuIAoKRGF0YSBwcmVwYXJhdGlvbjogCgoxKSBNYWtlIGEgdmVjdG9yIGNhbGxlZCAqc2VsX2NvdW50cnkqIHRoYXQgaW5jbHVkZXMgVW5pdGVkIEtpbmdkb20sIEluZGlhLCBOZXBhbCwgSXNyYWVsLCBHZXJtYW55LCBhbmQgQXVzdHJhbGlhCjIpIEZpbHRlciB5b3VyIGNvdmlkIGRhdGEgdG8gaW5jbHVkZSBvbmx5IGxvY2F0aW9ucyB0aGF0IGFyZSBpbiB5b3VyIHNlbF9jb3VudHJ5IHZlY3RvciwgYW5kIGZpbHRlciBmb3IgdGhlIHllYXIgdG8gYmUgZXF1YWwgdG8gMjAyMS4gQXNzaWduIHlvdXIgZmlsdGVyZWQgZGF0YSB0byBhIHZhcmlhYmxlIGNhbGxlZCAqd2Vla2x5X3ZheC4qIAozKSBBZ2dyZWdhdGUgeW91ciAqd2Vla2x5X3ZheCogZGF0YSBieSB3ZWVrIGFuZCBsb2NhdGlvbiB0byBmaW5kIHRoZSBtZWFuIG9mIHRoZSBgcGVvcGxlX3ZhY2NpbmF0ZWRfcGVyX2h1bmRyZWRgIGNvbHVtbi4gQXNzaWduIHRoZSByZXN1bHQgYmFjayB0byAqd2Vla2x5X3ZheCoKNCkgTWFrZSB0aGUgd2VlayBhbmQgbG9jYXRpb24gY29sdW1ucyBvZiAqd2Vla2x5X3ZheCogZmFjdG9ycwogClBsb3R0aW5nOgoKVXNpbmcgeW91ciAqd2Vla2x5X3ZheCogZGF0YSB5b3UgaGF2ZSBqdXN0IHByZXBhcmVkOgoKMSkgTWFrZSBhIHRpbWUgc2VyaWVzIHBsb3Qgd2l0aCB3ZWVrIGFzIHlvdXIgeCBheGlzIGFuZCB5b3VyIGFnZ3JlZ2F0aW9uIG9mIHRoZSBgcGVvcGxlX3ZhY2NpbmF0ZWRfcGVyX2h1bmRyZWRgIGNvbHVtbiBhcyB5b3VyIHkgYXhpcy4gCjIpIENvbG91ciBhbmQgZ3JvdXAgeW91ciBkYXRhIGJ5IGxvY2F0aW9uLiAKMykgTWFrZSBhbnkgYWVzdGhldGljIGNoYW5nZXMgeW91IHRoaW5rIHdpbGwgbWFrZSB0aGUgcGxvdCBiZXR0ZXIgYmFzZWQgb24gd2hhdCB3ZSBoYXZlIGNvdmVyZWQgc28gZmFyLCBzdWNoIGFzIGFkZGluZyB0aXRsZXMsIGNoYW5naW5nIGNvbG91cnMsIG9yIGFkZGluZyBmYWNldHMgKGBmYWNldF9ncmlkKClgIG9yIGBmYWNldF93cmFwKClgKS4KNCkgQXNzaWduIHlvdXIgZmluYWwgcGxvdCB0byBhIHZhcmlhYmxlIGFuZCBzYXZlIGl0ISAKCkhpbnQ6IGlmIHlvdXIgeCBheGlzIGlzIGxvb2tpbmcgc3F1YXNoZWQgb3IgY3JhbXBlZCwgdHJ5IGFkZGluZyBpbiBgc2NhbGVfeF9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2F4aXMobi5kb2RnZSA9IDIpKWAgCgpgYGB7cn0KIyB5b3VyIGNvZGUgaGVyZQoKCmBgYAoKCiMgRmluYWwgdGFzayAtIFBsZWFzZSBnaXZlIHVzIHlvdXIgaW5kaXZpZHVhbCBmZWVkYmFjayEKCldlIHdvdWxkIGJlIGdyYXRlZnVsIGlmIHlvdSBjb3VsZCB0YWtlIGEgbWludXRlIGJlZm9yZSB0aGUgZW5kIG9mIHRoZSB3b3Jrc2hvcCBzbyB3ZSBjYW4gZ2V0IHlvdXIgZmVlZGJhY2shCgo8aHR0cHM6Ly9sc2UuZXUucXVhbHRyaWNzLmNvbS9qZmUvZm9ybS9TVl9lZmxjMnlqNHBjcnljNjI/Y291cnNlbmFtZT1SJTIwRGF0YSUyMFZpc3VhbGlzYXRpb24lMjAyOiUyMEJveCwlMjBoaXN0b2dyYW0sJTIwYW5kJTIwbGluZSUyMHBsb3RzJnRvcGljPVImbGluaz1odHRwczovL2xzZWNsb3VkLnNoYXJlcG9pbnQuY29tLzp1Oi9zL1RFQU1fQVBELURTTC1EaWdpdGFsLVNraWxscy1UcmFpbmVycy9FUkRhTWVQRDVYQktneHVPTXROOTRZb0I0YURaNWR4WHFnUFhCRGR6V0Z4WVNRJnByb2c9RFMmdmVyc2lvbj0yMS0yMj4KClRoZSBzb2x1dGlvbnMgd2UgYmUgYXZhaWxhYmxlIGZyb20gYSBsaW5rIGF0IHRoZSBlbmQgb2YgdGhlIHN1cnZleS4KCiMgSW5kaXZpZHVhbCBjb2RpbmcgY2hhbGxlbmdlCgpGb3IgdGhlIGNvZGluZyBjaGFsbGVuZ2Ugd2Ugd2lsbCBsb29rIGF0IG90aGVyIHRoaW5ncyB5b3UgY2FuIGRvIHdpdGggZ2dwbG90MiBzdWNoIGFzIG1ha2luZyBhcnR3b3JrISBUaGlzIGlzIGtub3duIGFzIGdlbmVyYXRpdmUgYXJ0LCB3aGljaCBpcyBwcm9kdWNlZCBlaXRoZXIgaW4gcGFydCBvciBjb21wbGV0ZWx5IGJ5IGF1dG9tYXRlZCBwcm9jZXNzZXMuIAoKR2VuZXJhdGl2ZSBhcnQgaXMgYSBjb21wbGV4IHRvcGljLCBidXQgc29tZSBvZiB0aGUgaWRlYXMgYW5kIHN0eWxlcyBjYW4gYmUgZG9uZSB1c2luZyB0aGUgYVJ0c3kgcGFja2FnZSwgPGh0dHBzOi8va29lbmRlcmtzLmdpdGh1Yi5pby9hUnRzeS8+LCB3aGljaCBtYWtlcyBnZW5lcmF0aXZlIGFydCBtb3JlIGFjY2Vzc2libGUuIAoKRmlyc3QsIHlvdSB3aWxsIG5lZWQgdG8gaW5zdGFsbCB0aGUgYVJ0c3kgcGFja2FnZS4gCmBgYHtyIGV2YWw9RkFMU0V9CiMgaW5zdGFsbCBhUnRzeQppbnN0YWxsLnBhY2thZ2VzKCJhUnRzeSIpCmBgYAoKVGhlbiB5b3Ugd2lsbCBuZWVkIHRvIGxvYWQgaXQhIApgYGB7cn0KIyBsb2FkIGFSdHN5CmxpYnJhcnkoYVJ0c3kpCmBgYAoKV2hlbiBtYWtpbmcgZ2VuZXJhdGl2ZSBhcnQgaXQgaXMgYSBnb29kIGlkZWEgdG8gbWFrZSBpdCByZXByb2R1Y2libGUgYXMgd2UgdGhlcmUgaXMgYSBsb3Qgb2YgcmFuZG9taXNhdGlvbiBpbnZvbHZlZC4gV2hlbiByYW5kb21pc2luZyBpbiBSIHlvdSBuZWVkIHRvICpzZXQgYSBzZWVkKiwgd2hpY2ggaW4gc2ltcGxlIHRlcm1zIG1lYW5zIHdlIHJlcHJvZHVjZSBvdXIgcmVzdWx0cyB1c2luZyB0aGUgc2FtZSBzZWVkLiBXZSB1c2UgdGhlIGBzZXQuc2VlZCgpYCBmdW5jdGlvbiBhbmQgYWRkIGluIGFueSBudW1iZXIuIFRoZSBudW1iZXIgaXMgb3VyIHNlZWQuIElmIHdlIGdhdmUgc29tZW9uZSBlbHNlIG91ciBjb2RlIGFuZCBvdXIgc2VlZCB0aGV5IHdvdWxkIGJlIGFibGUgdG8gcmVwcm9kdWNlIG9yIHJlc3VsdHMuCgpXZSd2ZSBnaXZlbiBzb21lIGV4YW1wbGVzIGJlbG93IG9uIG1ha2luZyBhIHN0cmlwZWQgYXJ0d29yayBhbmQgZmxvdyBmaWVsZHMuIFJ1biB0aGUgY29kZSBjaHVuayBiZWxvdywgdGhlbiB0cnkgY2hhbmdpbmcgdGhlIHNlZWQgdG8gc2VlIGhvdyB0aGUgcmVzdWx0cyBjaGFuZ2Ugd2hlbiB5b3UgcnVuIGl0IGFnYWluISAKCk5vdGU6IHRoZXNlIHdpbGwgdGFrZSBhIGZldyBtb21lbnRzIHRvIHJ1biEgCmBgYHtyfQojIHNldCB0aGUgc2VlZCB0byAxCnNldC5zZWVkKDEpCgojIG1ha2UgYSBjb2xvdXIgcGFsZXR0ZSBmcm9tIHJjb2xvcmJyZXdlcgpzZXQxIDwtIGJyZXdlci5wYWwobiA9IDksIG5hbWUgPSAiU2V0MSIpCnBhc3RlbDEgPC0gYnJld2VyLnBhbChuID0gOSwgbmFtZSA9ICJQYXN0ZWwxIikKcGFpcmVkIDwtIGJyZXdlci5wYWwobiA9IDEyLCBuYW1lID0gIlBhaXJlZCIpCgojIHRlc3Qgb3V0IGRpZmZlcmVudCBwYXJhbWV0ZXJzIGZvciBzdHJpcGVzCmNhbnZhc19zdHJpcGVzKHBhaXJlZCwgbiA9IDgwMCwgSCA9IDUsIGJ1cm5pbiA9IDUpCgpjYW52YXNfc3RyaXBlcyhwYXN0ZWwxLCBuID0gNTAwLCBIID0gMTUsIGJ1cm5pbiA9IDIpCgojIFRlc3Qgb3V0IGRpZmZlcmVudCBwYXJhbWV0ZXJzIGZvciBmbG93IGZpZWxkcwpjYW52YXNfZmxvdyhzZXQxLCBiYWNrZ3JvdW5kID0gIiNmYWZhZmEiLCBsaW5lcyA9IDgwMCwgbHdkID0gMC4zMCwKICAgICAgICAgICAgaXRlcmF0aW9ucyA9IDgwLCBzdGVwbWF4ID0gMC4xNSkKCnBhc3RlbF9mbG93IDwtIGNhbnZhc19mbG93KHBhc3RlbDEsIGJhY2tncm91bmQgPSAiYmxhY2siLCBsaW5lcyA9IDIwMDAsIGx3ZCA9IDAuMTUsCiAgICAgICAgICAgIGl0ZXJhdGlvbnMgPSAzMCwgc3RlcG1heCA9IDAuMTApCgpwYXN0ZWxfZmxvdwoKIyBzYXZlIHBhc3RlbF9mbG93CnNhdmVDYW52YXMocGFzdGVsX2Zsb3csICJwYXN0ZWxfZmxvdy5wbmciKQpgYGAKCkhhdmUgYSBnbyB5b3Vyc2VsZiBhdCBtYWtpbmcgc29tZSBnZW5lcmF0aXZlIGFydCBpbiBSISBUcnkgb3V0IHRoZSBmb2xsb3dpbmcgZnVuY3Rpb25zIGZyb20gYVJ0c3ksIGNoYW5naW5nIHRoZSBwYXJhbWV0ZXJzIHRvIGFkanVzdCB0aGUgdmlzdWFsaXNhdGlvbi4gCgotIGBjYW52YXNfZmxvdygpYCA8aHR0cHM6Ly9rb2VuZGVya3MuZ2l0aHViLmlvL2FSdHN5L3JlZmVyZW5jZS9jYW52YXNfZmxvdy5odG1sPgotIGBjYW52YXNfc3RyaXBlcygpYCA8aHR0cHM6Ly9rb2VuZGVya3MuZ2l0aHViLmlvL2FSdHN5L3JlZmVyZW5jZS9jYW52YXNfc3RyaXBlcy5odG1sPgotIGBjYW52YXNfd2F0ZXJjb2xvcnMoKWAgPGh0dHBzOi8va29lbmRlcmtzLmdpdGh1Yi5pby9hUnRzeS9yZWZlcmVuY2UvY2FudmFzX3dhdGVyY29sb3JzLmh0bWw+CgpEb24ndCBmb3JnZXQgdG8gc2F2ZSBhbnkgb2YgeW91ciBhcnR3b3JrIHlvdSBsaWtlIHVzaW5nIHRoZSBgc2F2ZUNhbnZhcygpYCBmdW5jdGlvbi4gCmBgYHtyfQpzZXQuc2VlZCgxKQoKIyB5b3VyIGNvZGUgaGVyZQoKCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBSZWNvbW1lbmVkIHJlc291cmNlcyB0byBjb250aW51ZSB5b3VyIGRhdGEgdmlzdWFsaWF0aW9uIGxlYXJuaW5nCgpUaGUgZ2dwbG90MiBib29rIGlzIGFuIGV4Y2VsbGVudCByZXNvdXJjZSB3aXRoIGxvdHMgb2YgZXhhbXBsZXMgYW5kIGV4ZXJjaXNlcyB0byBoYXZlIGEgZ28gYXQgPGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy8+LiAKCkNlZHJpYyBTY2hlcmVyIHdyaXRlcyBibG9ncyBhbmQgdHV0b3JpYWxzIG9uIGdncGxvdDIgb24gaGlzIHdlYnNpdGUuIFNvbWUgb2YgaGlzIGNvbnRlbnQgaXMgcmVhbGx5IGdyZWF0IGFuZCB3b3J0aCBsb29raW5nIHRocm91Z2guIEJlbG93IGFyZSB0d28gb2YgaGlzIHR1dG9yaWFscyB0byBnZXQgeW91IHN0YXJ0ZWQ6CgotIDxodHRwczovL3d3dy5jZWRyaWNzY2hlcmVyLmNvbS8yMDE5LzA4LzA1L2EtZ2dwbG90Mi10dXRvcmlhbC1mb3ItYmVhdXRpZnVsLXBsb3R0aW5nLWluLXIvPgotIDxodHRwczovL3d3dy5jZWRyaWNzY2hlcmVyLmNvbS8yMDE5LzA1LzE3L3RoZS1ldm9sdXRpb24tb2YtYS1nZ3Bsb3QtZXAuLTEvPgoKR2Vvcmdpb3MgS2FyYW1hbmlzIGlzIGEgZGF0YSB2aXN1YWxpc2F0aW9uIGRlc2lnbmVyIGFuZCBtYWtlcyBzb21lIGFtYXppbmcgdmlzdWFsaXNhdGlvbnMgdXNpbmcgUiEgSXQncyB3b3J0aCBicm93c2luZyBoaXMgd2Vic2l0ZSBmb3IgaW5zcGlyYXRpb24gPGh0dHBzOi8va2FyYW1hbi5pcy8+IG9yIGZvbGxvd2luZyBoaW0gb24gdHdpdHRlciA8aHR0cHM6Ly90d2l0dGVyLmNvbS9nZW9rYXJhbWFuaXM+LgoKRm9yIGlkZWFzIGFib3V0IHdoYXQgdG8gZG8gd2l0aCB5b3VyIGRhdGEgaGF2ZSBhIGxvb2sgYXQgdGhlIFIgZ3JhcGggZ2FsbGVyeSA8aHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8+LiAKCgo=